OpenAPITools / OpenAPITools/openapi-generator

[BUG][typescript-fetch] Path parameters incorrectly typed as `string | null` when `anyOf` with null exists in unrelated schema

Open
#22,427 1 comment 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
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 anyOf syntax for nullable types
  • Using type: ["string", "null"] (equivalent valid OpenAPI 3.1.0 syntax) does NOT trigger the bug
  • Path parameters have required: true and schema: {type: "string"} but still get | null added
  • 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
  1. Save the minimal spec above as spec.json
  2. Run: npx @openapitools/openapi-generator-cli generate -i spec.json -g typescript-fetch -o ./out
  3. 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:

  1. Processing of anyOf/oneOf with null types doesn't affect unrelated schemas
  2. Path parameters with required: true are never typed as nullable regardless of other schemas in the spec

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.