Skip to content

fix: honor sibling nullable:true on single-element allOf/oneOf/anyOf#465

Merged
Carapacik merged 1 commit into
Carapacik:mainfrom
CallMeSH:fix/sibling-nullable-allof
Jun 21, 2026
Merged

fix: honor sibling nullable:true on single-element allOf/oneOf/anyOf#465
Carapacik merged 1 commit into
Carapacik:mainfrom
CallMeSH:fix/sibling-nullable-allof

Conversation

@CallMeSH

Copy link
Copy Markdown
Contributor

Problem

OpenAPI 3.0 expresses a nullable composed type with a nullable: true sibling next to a single-element allOf / oneOf / anyOf:

field:
  nullable: true
  type: object
  allOf:
    - $ref: '#/components/schemas/Foo'

swagger_parser generates this field as non-nullable (Foo instead of Foo?).

The equivalent OpenAPI 3.1 form already works:

field:
  oneOf:
    - $ref: '#/components/schemas/Foo'
    - type: null

Root cause

In _findType (open_api_parser.dart), the allOf/oneOf/anyOf branch splits on element count:

  • Multi-element (oneOf: [{$ref}, {type: null}]): the {type: null} member is detected and makeNullable is applied. This branch also already honors a sibling nullable: true.
  • Single-element (allOf: [{$ref}]): it returns the inner ref type verbatim. The inner ref's own nullable is false, and the final nullability resolution (ofType?.nullable ?? map[nullable] ?? …) short-circuits on that non-null false, so the outer nullable: true is never consulted.

Same intent, different element count, different code path — which is why the 3.1 two-element form works but the 3.0 single-element sibling form does not.

Fix

Apply makeNullable in the single-element branch when the outer schema carries nullable: true, mirroring what the multi-element branch already does.

Tests

Adds an e2e regression test xof/nullable_sibling_all_of.3.0 covering both the 3.0 single-element sibling form and the 3.1 oneOf + type: null form. Also updates the existing all_of.3.0 expected output, whose getUserById response is {nullable: true, allOf: [{$ref: UserDto}]} and had silently captured the same bug — now correctly Future<UserDto?>. Full suite passes; dart format and dart analyze clean.

OpenAPI 3.0 expresses a nullable composed type as a `nullable: true` sibling
next to a single-element `allOf`/`oneOf`/`anyOf`, e.g.
`{nullable: true, allOf: [{$ref: ...}]}`. The parser's single-element xOf
branch returned the inner ref type verbatim, whose own `nullable` is false, and
the final nullability resolution short-circuited on that non-null `false` before
ever consulting the outer `nullable: true`. The field was generated as
non-nullable (e.g. `Foo` instead of `Foo?`).

The OpenAPI 3.1 form `{oneOf: [{$ref}, {type: null}]}` already worked because it
is a two-element xOf and hits the multi-element branch, which detects the null
member and calls makeNullable. Apply the same makeNullable in the single-element
branch when the outer map carries `nullable: true`, making the two encodings
consistent.

Adds an e2e regression test for the sibling-nullable single-element form and
updates the existing all_of.3.0 expected output, whose `getUserById` response
(`{nullable: true, allOf: [{$ref: UserDto}]}`) had captured the same bug and is
now correctly nullable.

Signed-off-by: Charles-Henri <dumalin.ch@gmail.com>
@Carapacik Carapacik merged commit ba9aea3 into Carapacik:main Jun 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants