Nullable $ref property (anyOf/oneOf with type: null) is silently omitted from the generated type

#950 · closed · 2 comments

View on GitHub ↗

paulyii

### Summary A property declared as a nullable reference — `anyOf: [{$ref: …}, {type: "null"}]` — is **silently omitted** from the generated Swift struct. No warning, no error, no diagnostic of any kind. The build succeeds and the property simply does not exist. The same happens with the `oneOf` spelling. Sibling properties on the same schema generate normally, so the failure is per-property and easy to miss. This is the standard shape FastAPI/Pydantic emits for an `Optional[SomeModel]` field, so it is not an exotic construct. ### Reproduction Minimal OpenAPI 3.1 document: ```json { "openapi": "3.1.0", "info": { "title": "Repro", "version": "1.0.0" }, "paths": { "/thing": { "get": { "operationId": "getThing", "responses": { "200": { "description": "ok", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Thing" } } } } } } } }, "components": { "schemas": { "Nested": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"] }, "Thing": { "type": "object", "properties": { "id": { "type": "integer" }, "plain_nullable": { "type": ["string", "null"] }, "ref_nullable_anyof": { "anyOf": [{ "$ref": "#/components/schemas/Nested" }, { "type": "null" }] }, "ref_nullable_oneof": { "oneOf": [{ "$ref": "#/components/schemas/Nested" }, { "type": "null" }] }, "ref_plain": { "$ref": "#/components/schemas/Nested" } }, "required": ["id"] } } } } ``` Config: ```yaml generate: - types - client accessModifier: public ``` ### Expected `Thing` has five properties, with the nullable references generated as optionals of `Components.Schemas.Nested`. ### Actual `Thing` has three. Both nullable-reference properties are gone: ```swift public struct Thing: Codable, Hashable, Sendable { public var id: Swift.Int public var plain_nullable: Swift.String? public var ref_plain: Components.Schemas.Nested? } ``` `ref_nullable_anyof` and `ref_nullable_oneof` are absent, and absent from `CodingKeys` too. `plain_nullable` (type-array nullability) and `ref_plain` (non-nullable `$ref`) both generate correctly — so the trigger is specifically *reference + null*. ### Versions Reproduced identically on **1.12.2** and **1.13.1**, via the SPM build-tool plugin, Xcode 26, generating for an iOS target. ### Workaround Inlining the referenced schema in place of the `$ref`, with `"type": ["object", "null"]`, generates the property correctly (as a nested `…Payload` type). That costs the shared named type at the embedding site, but it is the only shape we found that works. Rewriting `anyOf` → `oneOf` does not help. ### Impact The silence is the real problem. We shipped a hand-written workaround — an extra list fetch plus normalized-string matching — to reconstruct an object the API had been sending all along, because the generated type simply had no field for it. That went unnoticed for about two months. A diagnostic would have turned this into a five-minute fix. Even if generating these is out of scope for now, emitting a warning for a property that is being dropped would prevent the silent-data-loss failure mode.

Comments

simonjbeaumont

> A diagnostic would have turned this into a five-minute fix. > > Even if generating these is out of scope for now, emitting a warning for a property that is being dropped would prevent the silent-data-loss failure mode. I thought we emitted diagnostics for everything we skip? Are you confident there was no diagnostic?

czechboy0

Hi @paulyii, a friendly reminder of our CONTRIBUTING policy: https://github.com/apple/swift-openapi-generator/blob/main/CONTRIBUTING.md#ai-tools All human-facing communication, such as issues, PR descriptions, and comments must be written by humans and not LLMs. Please make sure to follow this policy in future interactions on this repo, thanks!