Repository navigation
Multi-value nullable unions emit an object variant for an explicit null-only branch #414
Description
Regression
OpenAI's 3.1 schema replaces a legacy nullable: true on a two-value union with a third type: null branch. AutoSDK generates a catch-all object as an additional CLR union variant instead of preserving the nullable union over the two non-null variants.
Seen in tryAGI/OpenAI#416 (head eaefd98ae2c87a719658472ab5a671921f849ae9), for RealtimeSessionCreateRequestGA.Tracing, its Realtime projection, CLI command and serializer contexts. All 11 changed files were inspected. The PR has no package/workflow changes; normal build CI succeeds, so compilation alone does not establish nullability/schema fidelity. The existing two-variant collapse from #390 does not cover this shape.
Minimal reproduction
openapi: 3.1.0
info: {title: Null Union Reproduction, version: 1.0.0}
paths: {}
components:
schemas:
Request:
type: object
properties:
tracing:
oneOf:
- type: string
enum: [auto]
- type: object
properties:
workflow_name: {type: string}
- type: 'null'Generate with autosdk generate openapi.yaml --namespace NullUnionRepro --output Generated.
Confirmed with CLI 0.35.0-preview.2.67+83957f817c7b8e3fb764c3809111911ac810d84b; the current organization regeneration also emits the same shape in OpenAI #416. The current generator's IsNullableUnion helper requires exactly two variants.
Observed generated property:
OneOf<RequestTracingEnum?, RequestTracingEnum2, object>? TracingExpected: a nullable union of the enum and the typed configuration, with no catch-all object branch introduced by the null-only schema. The additional generic argument also changes the public request/method signature for an equivalent wire schema.
Required fix and validation
- Exclude exact null-only branches from multi-value
oneOfandanyOfCLR variant lists while retaining outer nullability. - Preserve actual unconstrained
objectalternatives, named/reference unions, branch naming/order and multiple concrete-value variants; do not collapse a true multi-value union to its first branch. - Cover legacy nullable versus explicit null equivalence, two and three-plus concrete variants, enum/object/reference choices, null-only edge cases, round trips and invalid primitive inputs.
- Validate AutoSDK units/snapshots and compiled serializer/NativeAOT behavior, regenerate OpenAI from its spec inputs, and wait for Publish/trimming before calling the regression fixed. Generated files must not be edited by hand.
Default reproduction is local and key-free; no provider call is necessary.
Compiled local runtime evidence
The generated minimal reproduction compiles and runs on .NET10 with its source-generated serializer context and no external packages. Observations:
{"tracing":true} => ArgumentOutOfRangeException
{"tracing":"unexpected"} => introduced_object=True
{"tracing":null} => introduced_object=False
{"tracing":"auto"} => introduced_object=False
{"tracing":{"workflow_name":"test"}} => introduced_object=False
The invalid string taking the newly introduced object branch confirms the schema-widening behavior. The boolean exception is a separate existing enum-converter fallback/error-shape concern, not a successful invalid-input round trip. No provider request was made.
OpenAI#416 was merged by the existing auto-merge workflow as 5bb64663a6d446210fce811628d5e29ca2cfd76f at2026-10-04T03:26:48Z while an attempted draft conversion raced it. The draft conversion command succeeded, but the final PR state is merged; this is not a prevention guarantee. PR Test succeeded; Publish is monitored separately. This issue remains open until the generator fix and regenerated SDK validation are complete.
Activity
HavenDV commentedon Oct 4, 2026
Resolved on main by 1ab99a2, with shard-compatible integration test names in 40bbf26 and regenerated corpus snapshots in 2d9e3ab.
Explicit null-only branches are excluded from multi-value anyOf/oneOf alternatives while outer nullability and concrete alternative ordering are retained. Type/helper arity now comes from the retained alternatives. Actual unconstrained object alternatives remain present. Single-concrete nullable wrappers retain their existing mapping; all-null unions are not collapsed to an empty union.
Validation:
This change does not add strict closed-enum/union validation to legacy converters. Unknown-value behavior remains a separate concern. A separately launched fully rooted OpenAI native compilation is still running, so it is not being reported as a successful SDK native execution.