Skip to content

Fix duplicate schema component for nullable unions in OpenAPI - #68665

Merged
wtgodbe merged 1 commit into
dotnet:release/11.0from
DeagleGross:deaglegross-openapi-nested-union-duplicate-schema
Aug 21, 2026
Merged

Fix duplicate schema component for nullable unions in OpenAPI#68665
wtgodbe merged 1 commit into
dotnet:release/11.0from
DeagleGross:deaglegross-openapi-nested-union-duplicate-schema

Conversation

@DeagleGross

Copy link
Copy Markdown
Member

Problem

A nullable union property duplicated its case type component — OneOrManyBeast referenced OneOrManyBeastBeast instead of Beast, even though the array branch referenced Beast correctly:

"OneOrManyBeast": {
  "anyOf": [
    { "$ref": "#/components/schemas/OneOrManyBeastBeast" },
    { "type": "array", "items": { "$ref": "#/components/schemas/Beast" } }
  ]
},
"OneOrManyBeastBeast": { ... identical to Beast ... }

Cause

Unions are value types, so MyUnion? surfaces as Nullable<MyUnion> whose JsonTypeInfo.Kind is None, not Union. The x-schema-is-union marker was never written, so the existing union carve-out in ResolveReferenceForSchema didn't apply.

The problem was nullability of the union type.

Fix

Unwrap Nullable<> before the Kind check, mirroring GetSchemaReferenceId. Uses TryGetTypeInfo so a resolver missing the underlying type degrades instead of throwing.

Fixes #68653

@DeagleGross DeagleGross self-assigned this Aug 20, 2026
Copilot AI lite review requested due to automatic review settings August 20, 2026 14:09
@DeagleGross
DeagleGross changed the base branch from main to release/11.0 August 20, 2026 14:09

@Youssef1313 Youssef1313 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't debugged through it, but the change looks reasonable on a high level.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an OpenAPI schema component duplication that occurs when a nullable C# union is used in a nested position and one union branch is a polymorphic base type. The change ensures nullable unions are still detected as unions during schema export so reference resolution can reuse the existing polymorphic component rather than generating a duplicate.

Changes:

  • Unwraps Nullable<T> when determining whether to write the x-schema-is-union marker during schema export.
  • Adds shared polymorphic “Beast” union types plus targeted unit tests covering direct, nested, nullable, and non-nullable union scenarios.
  • Updates sample endpoint and integration snapshots to include the new unions/beasts scenario and validate generated schemas.
Show a summary per file
File Description
src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs Unwraps Nullable<> before checking JsonTypeInfo.Kind so nullable unions are still marked with x-schema-is-union.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Shared/SharedTypes.Unions.cs Adds a polymorphic base type (Beast) and a union (OneOrManyBeast) plus containers to reproduce/validate the duplication scenario.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.UnionSchemas.cs Adds unit tests asserting both union branches reference the polymorphic base component and that no duplicate component is generated.
src/OpenApi/sample/Endpoints/MapUnionsEndpoints.cs Adds a sample endpoint exercising a nullable union with a polymorphic case type.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApiDocumentLocalizationTests.VerifyOpenApiDocumentIsInvariant.verified.txt Updates invariant snapshot to include the new /unions/beasts path and related schemas.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_0/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=unions.verified.txt Updates OpenAPI 3.0 unions snapshot for the new beasts scenario.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_1/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=unions.verified.txt Updates OpenAPI 3.1 unions snapshot for the new beasts scenario.
src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Integration/snapshots/OpenApi3_2/OpenApiDocumentIntegrationTests.VerifyOpenApiDocument_documentName=unions.verified.txt Updates OpenAPI 3.2 unions snapshot for the new beasts scenario.

Review details

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 8/8 changed files
  • Comments generated: 0
  • Review effort level: Lite

@wtgodbe
wtgodbe merged commit b8023d4 into dotnet:release/11.0 Aug 21, 2026
34 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-rc2 milestone Aug 21, 2026
wtgodbe added a commit that referenced this pull request Aug 22, 2026
* [SignalR] Reject duplicate SignalR upload stream IDs (#68525) (#68638)

* Reject duplicate SignalR upload stream IDs



* Simplify upload stream ownership cleanup



* Avoid upload stream ownership allocations



* Simplify upload stream registration ownership



* Defer upload stream reader creation





* Dispose cancellation source after binding failure





* Reuse upload stream test helper





---------

Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 82e97f5a-a052-4dbe-9cf1-b62f45cf7ee2

* Honor all sign-in confirmation requirements after registration (#68631) (#68655)

Co-authored-by: Brennan <brecon@microsoft.com>

* Preserve BadHttpRequestException status codes (#68632) (#68649)

* Preserve BadHttpRequestException status codes



* Preserve exception handler 404 safeguard



---------

Co-authored-by: Stephen Halter <halter73@gmail.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* SignInManager: return SignInResult.Failed for expired passkey session challenge (#67539) (#68654)

Co-authored-by: Grant Totinov <granttotinov604@gmail.com>

* Don't apply the CSRF verdict to remote authentication callbacks (#68669)

* Don't apply the CSRF verdict to remote authentication callbacks

A remote provider's callback (OIDC response_mode=form_post, WS-Federation)
is a cross-site form POST by protocol design, so the auto-injected CSRF
protection records an invalid IAntiforgeryValidationFeature verdict for it.
The handler then throws while reading its own callback body, before any of
its events can run, so apps have no way to opt out.

Suppress the verdict while a remote handler owns the request, and restore it
if the handler declines so the rest of the pipeline still sees it.

Fixes #68666

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd

* test both antiforgery & csrf

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd

* Use model display names in Blazor input parsing errors (#68667) (#68688)

* Use display attributes in input parsing errors

* Address test coverage feedback from review.

* Apply dedup cleanup from feedback.

Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com>

* [release/11.0-rc1] Extract IsAuthenticated helper method (#68658)

* Extract IsAuthenticated helper method

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Reorder using

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

* Use SecurityHelper for authentication revalidation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>

---------

Co-authored-by: Youssef1313 <youssefvictor00@gmail.com>
Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>
Co-authored-by: Milos Kotlar <kotlarmilos@gmail.com>

* Fix  InitialItemIndex viewport underfill for small items in big container or on window resize (#67936) (#68689)

Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com>

* fix nullable<union> for openapi gen (#68665)

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Brennan <brecon@microsoft.com>
Co-authored-by: Stephen Halter <halter73@gmail.com>
Co-authored-by: Grant Totinov <granttotinov604@gmail.com>
Co-authored-by: Korolev Dmitry <dmkorolev@microsoft.com>
Co-authored-by: Ilona Tomkowicz <32700855+ilonatommy@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Youssef1313 <youssefvictor00@gmail.com>
Co-authored-by: Youssef1313 <31348972+Youssef1313@users.noreply.github.com>
Co-authored-by: Milos Kotlar <kotlarmilos@gmail.com>
Co-authored-by: William Godbe <wigodbe@microsoft.com>
Copilot-Session: 82e97f5a-a052-4dbe-9cf1-b62f45cf7ee2
Copilot-Session: cb987098-3301-465b-9a3d-2e63aabf43bd
@DeagleGross
DeagleGross deleted the deaglegross-openapi-nested-union-duplicate-schema branch August 22, 2026 21:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAPI duplicates a polymorphic union case when the union is used as a property

4 participants