OpenAPITools / OpenAPITools/openapi-generator
[BUG][typescript-fetch] Path parameters incorrectly typed as `string | null` when `anyOf` with null exists in unrelated schema
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request
Description
When generating a typescript-fetch client from an OpenAPI 3.1.0 spec that contains anyOf: [{type: "string"}, {type: "null"}] anywhere in components.schemas, path parameters in unrelated operations are incorrectly typed as string | null instead of string.
The nullable schema doesn't need to be referenced by the affected paths - its mere existence contaminates path parameter type inference. This appears to be some form of generator state corruption during processing.
Key observations:
- The bug only affects
anyOfsyntax for nullable types - Using
type: ["string", "null"](equivalent valid OpenAPI 3.1.0 syntax) does NOT trigger the bug - Path parameters have
required: trueandschema: {type: "string"}but still get| nulladded - The contaminating schema is never referenced by the affected paths
Common source of this pattern: The zod-openapi library (used to generate OpenAPI specs from Zod schemas) produces anyOf: [{type: "string"}, {type: "null"}] when converting .nullable() Zod schemas to OpenAPI 3.1.0. This is valid OpenAPI 3.1.0 syntax, but triggers this generator bug.
openapi-generator version
7.17.0
Also confirmed on 7.18.0-SNAPSHOT (latest master via docker pull openapitools/openapi-generator-cli:latest)
OpenAPI declaration file content or url
Minimal reproduction (38 lines):
{
"openapi": "3.1.0",
"info": {"title": "Bug Repro", "version": "1.0.0"},
"paths": {
"/a/{p1}/b/{p2}/c/{p3}": {
"get": {
"operationId": "getFirst",
"parameters": [
{"in": "path", "name": "p1", "schema": {"type": "string"}, "required": true},
{"in": "path", "name": "p2", "schema": {"type": "string"}, "required": true},
{"in": "path", "name": "p3", "schema": {"type": "string"}, "required": true}
],
"responses": {"200": {"description": "OK"}}
}
},
"/x/{p1}/y/{p2}/z/{p3}": {
"get": {
"operationId": "getSecond",
"parameters": [
{"in": "path", "name": "p1", "schema": {"type": "string"}, "required": true},
{"in": "path", "name": "p2", "schema": {"type": "string"}, "required": true},
{"in": "path", "name": "p3", "schema": {"type": "string"}, "required": true}
],
"responses": {"200": {"description": "OK"}}
}
}
},
"components": {
"schemas": {
"NullableField": {
"type": "object",
"properties": {
"value": {"anyOf": [{"type": "string"}, {"type": "null"}]}
}
}
}
}
}
Note: The NullableField schema is never referenced by any path, yet it causes the bug.
Generation Details
npx @openapitools/openapi-generator-cli generate \
-i spec.json \
-g typescript-fetch \
-o ./generated
Config file (optional, bug reproduces without it):
{
"supportsES6": true,
"enumPropertyNaming": "PascalCase",
"modelPropertyNaming": "camelCase",
"useSingleRequestParameter": true
}
Steps to reproduce
- Save the minimal spec above as
spec.json - Run:
npx @openapitools/openapi-generator-cli generate -i spec.json -g typescript-fetch -o ./out - Check
./out/apis/DefaultApi.ts
Actual output
export interface GetFirstRequest {
p1: string;
p2: string | null; // BUG: should be string
p3: string | null; // BUG: should be string
}
export interface GetSecondRequest {
p1: string;
p2: string;
p3: string;
}
Expected output
export interface GetFirstRequest {
p1: string;
p2: string;
p3: string;
}
export interface GetSecondRequest {
p1: string;
p2: string;
p3: string;
}
Workaround
Using the type array syntax instead of anyOf for nullable fields avoids the bug:
// Instead of this (triggers bug):
"value": {"anyOf": [{"type": "string"}, {"type": "null"}]}
// Use this (works correctly):
"value": {"type": ["string", "null"]}
Both are valid OpenAPI 3.1.0 for expressing nullable types.
For users of zod-openapi or similar libraries, a post-processing step can convert anyOf nullable patterns to type arrays:
function fixNullableAnyOf(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(fixNullableAnyOf);
if (obj.anyOf && Array.isArray(obj.anyOf) && obj.anyOf.length === 2) {
const types = obj.anyOf.map(item => item.type).filter(Boolean);
if (types.length === 2 && types.includes('null')) {
const nonNullType = types.find(t => t !== 'null');
if (nonNullType && typeof nonNullType === 'string') {
const { anyOf, ...rest } = obj;
return { ...fixNullableAnyOf(rest), type: [nonNullType, 'null'] };
}
}
}
const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = fixNullableAnyOf(value);
}
return result;
}
Related issues/PRs
Could not find directly related issues. May be related to OpenAPI 3.1.0 processing (generator shows warning: "OpenAPI 3.1 support is still in beta").
Suggest a fix
The bug appears to be state corruption during schema processing. When the generator encounters anyOf containing {type: "null"}, some internal nullable flag is being set globally or not being properly scoped, which then affects subsequent path parameter type generation.
The fix should ensure that:
- Processing of
anyOf/oneOfwith null types doesn't affect unrelated schemas - Path parameters with
required: trueare never typed as nullable regardless of other schemas in the spec
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Save the provided 38-line document as spec.json and reproduce the issue with the typescript-fetch generation command. Inspect ./out/apis/DefaultApi.ts, then trace how nullable anyOf schemas are processed during path-parameter generation. Done means unrelated required string parameters remain string, while nullable schemas still work correctly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi, typescript
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100