Skip to content

OpenAPI 3.1 allOf reference makes later refs to a nullable component non-nullable #17416

Description

@ngyna

Which Fern component?

Fern CLI / OpenAPI importer

What's the issue?

For an OpenAPI 3.1 component using type: [object, "null"], a documented allOf reference can make later references to that component non-nullable. The generated contract therefore depends on traversal order.

Minimal reproduction:

openapi: 3.1.0
info:
  title: Nullable reference repro
  version: 1.0.0
paths:
  /profile:
    get:
      operationId: getProfile
      responses:
        "200":
          description: Profile
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Profile"
components:
  schemas:
    Profile:
      type: object
      required: [first, composed, second]
      properties:
        first:
          $ref: "#/components/schemas/FirstProfile"
        composed:
          $ref: "#/components/schemas/ComposedProfile"
        second:
          $ref: "#/components/schemas/SecondProfile"

    FirstProfile:
      type: object
      required: [details]
      properties:
        details:
          $ref: "#/components/schemas/NullableDetails"

    ComposedProfile:
      type: object
      required: [details]
      properties:
        details:
          allOf:
            - $ref: "#/components/schemas/NullableDetails"
            - description: Nullable details

    SecondProfile:
      type: object
      required: [details]
      properties:
        details:
          $ref: "#/components/schemas/NullableDetails"

    NullableDetails:
      type: [object, "null"]
      required: [value]
      properties:
        value:
          type: string

With Fern CLI 5.89.0 and fern-typescript-sdk 3.85.2, the generated types are:

export interface FirstProfile {
    details: ExampleApi.NullableDetails | null;
}

export interface SecondProfile {
    details: ExampleApi.NullableDetails;
}

SecondProfile.details should also include null. The IR similarly changes from nullable<named<NullableDetails>> before the allOf reference to plain named<NullableDetails> afterward.

The equivalent OpenAPI 3.0 schema using:

type: object
nullable: true

keeps every reference nullable. Encoding the 3.1 schema as anyOf: [{type: object, ...}, {type: "null"}] also avoids the problem.

This reproduces with the latest published CLI 5.95.1. The relevant importer code is unchanged on current main.

A likely implementation clue is that convertSchemaObject() normalizes schema.type by mutating the resolved Schema Object in place. The allOf conversion appears to leave later references observing the normalized object without its original nullability. Avoiding mutation of shared resolved schemas, or preserving nullability independently, may resolve the order dependence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions