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.
Which Fern component?
Fern CLI / OpenAPI importer
What's the issue?
For an OpenAPI 3.1 component using
type: [object, "null"], a documentedallOfreference can make later references to that component non-nullable. The generated contract therefore depends on traversal order.Minimal reproduction:
With Fern CLI 5.89.0 and
fern-typescript-sdk3.85.2, the generated types are:SecondProfile.detailsshould also includenull. The IR similarly changes fromnullable<named<NullableDetails>>before theallOfreference to plainnamed<NullableDetails>afterward.The equivalent OpenAPI 3.0 schema using:
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()normalizesschema.typeby mutating the resolved Schema Object in place. TheallOfconversion 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.