ASP.NET Core 11 OpenAPI 3.2: What Breaks and How to Keep 3.1

Last updated: October 26, 2026 · Tested with ASP.NET Core 11 RC2 (11.0.0-rc.2.26504.105) against ASP.NET Core 10.0.13, Microsoft.OpenApi 1.6.31 / 2.13.0 / 3.11.0, NSwag 14.7.1, Kiota 1.36.1, openapi-typescript 7.13.0, Redocly CLI 2.71.0, Windows 11

Short answer: in ASP.NET Core 11, AddOpenApi() writes "openapi": "3.2.0" instead of 3.1. Current generators (NSwag 14.7, Kiota 1.36, openapi-typescript 7.13) read it without errors. Anything that parses the document with Microsoft.OpenApi 2.x fails with OpenApiUnsupportedSpecVersionException: OpenAPI specification version '3.2.0' is not supported. That includes .NET 10 code, since .NET 10’s own OpenAPI package and Swashbuckle 10 use 2.x. If a consumer can’t move to Microsoft.OpenApi 3.x yet, one line keeps the document on 3.1: options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1. Even then, expect generated clients to change, because the document itself got more accurate.

Consumer ASP.NET Core 11 default (3.2.0) With OpenApi3_1 (3.1.2)
Microsoft.OpenApi 1.6.31 (old tooling) Fails: ‘3.2.0’ is not supported Fails: ‘3.1.2’ is not supported (it already failed on .NET 10’s 3.1.1)
Microsoft.OpenApi 2.13.0 (.NET 10 stack) Fails: ‘3.2.0’ is not supported Reads it
Microsoft.OpenApi 3.11.0 Reads it Reads it
NSwag 14.7.1, Kiota 1.36.1, openapi-typescript 7.13.0 Generate a client Generate a client
Redocly CLI 2.71.0 lint Parses it; only findings from the recommended style rules Same

How I Tested It

One minimal API project builds for net10.0 and net11.0 from the same source (a Directory.Build.props switches the target framework and the Microsoft.AspNetCore.OpenApi version). It has a plain JSON endpoint, a file download, a Server-Sent Events stream, an [Obsolete] endpoint and a controller action with two response types for the same status code. A script runs each build, saves /openapi/v1.json, and feeds the three documents (.NET 10, .NET 11 default, .NET 11 with 3.1) to each tool. Everything below is copied from those files and tool runs.

builder.Services.AddOpenApi();

var app = builder.Build();
app.MapOpenApi();

The Error: OpenAPI Specification Version ‘3.2.0’ Is Not Supported

The first thing that breaks is any .NET code that loads the document with Microsoft.OpenApi 2.x: a contract test, an API gateway that merges documents, an internal tool. The core of my test reader:

var result = await OpenApiDocument.LoadAsync(stream, "json");
var doc = result.Document;
var diagnostic = result.Diagnostic;
var version = diagnostic?.SpecificationVersion.ToString();

With Microsoft.OpenApi 2.13.0:

Microsoft.OpenApi 2.13.0.0
== openapi_net10.json
   read as: OpenApi3_1, paths: 5
== openapi_net11.json
   OpenApiUnsupportedSpecVersionException: OpenAPI specification version '3.2.0' is not supported.
== openapi_net11_keep31.json
   read as: OpenApi3_1, paths: 5

2.x is not a niche version: Microsoft.AspNetCore.OpenApi 10.0.13 depends on Microsoft.OpenApi [2.12.2, 3.0.0), and Swashbuckle.AspNetCore.Swagger 10.3.1 on Microsoft.OpenApi 2.7.5. So a .NET 10 service that reads a .NET 11 service’s document hits this the day the other team upgrades. Microsoft.OpenApi 3.11.0 reads all three documents:

Microsoft.OpenApi 3.11.0.0
== openapi_net10.json
   read as: OpenApi3_1, paths: 5
== openapi_net11.json
   read as: OpenApi3_2, paths: 5
== openapi_net11_keep31.json
   read as: OpenApi3_1, paths: 5

Moving that consumer to Microsoft.OpenApi 3.x is the real fix, but it is a code change: the 3.x object model uses interfaces (IOpenApiMediaType, IOpenApiSchema), which is what breaks OpenAPI transformers on upgrade with CS0266. I covered that error in Upgrading to .NET 11: 9 breaking changes.

The really old reader, Microsoft.OpenApi 1.6, is a different story: it rejects even the 3.1.1 that .NET 10 writes (OpenAPI specification version '3.1.1' is not supported.), so if something still uses it, it broke when .NET 10 moved to OpenAPI 3.1, not now.

Keep the Document on OpenAPI 3.1

If you own the API and a consumer can’t upgrade yet, set the version on AddOpenApi:

using Microsoft.OpenApi;

builder.Services.AddOpenApi(options => options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1);

The document then says "openapi": "3.1.2". Compared with the default 3.2 document from the same app, the only other difference (besides the port of the second run) is that the Server-Sent Events item schema is written as an x-oai-itemSchema extension instead of the 3.2 keyword:

2c2
<  "openapi": "3.2.0",
---
>  "openapi": "3.1.2",
9c9
<    "url": "http://localhost:5411"
---
>    "url": "http://localhost:5412"
84c84
<         "itemSchema": {
---
>         "x-oai-itemSchema": {

Everything else that changed in ASP.NET Core 11 (next section) stays, so switching back to 3.1 doesn’t give you the .NET 10 document.

What Else Changed in the Document

All response types for one status code are kept

This controller action returns JSON or CSV with 200, and two error shapes with 400:

[HttpGet("{id}")]
[ProducesResponseType<Order>(StatusCodes.Status200OK, "application/json")]
[ProducesResponseType<OrderCsvRow>(StatusCodes.Status200OK, "text/csv")]
[ProducesResponseType<ValidationProblemDetails>(StatusCodes.Status400BadRequest)]
[ProducesResponseType<ValidationError>(StatusCodes.Status400BadRequest)]
public IActionResult Get(int id) => Ok(new Order(id, 19.99m));

On .NET 10 only the last attribute per status code survived: the 200 response lists text/csv only, and the 400 response only ValidationError. The JSON response was simply missing from the document. ASP.NET Core 11 writes both 200 media types and combines the two 400 types with anyOf:

"anyOf": [
 {
  "$ref": "#/components/schemas/ValidationError"
 },
 {
  "$ref": "#/components/schemas/ValidationProblemDetails"
 }
]

Server-Sent Events use the 3.2 itemSchema

For TypedResults.ServerSentEvents(...), .NET 10 referenced a generated SseItemOfOrder component. ASP.NET Core 11 describes each event with itemSchema instead (x-oai-itemSchema in 3.1 mode), and the SseItemOfOrder component is gone.

[Obsolete] endpoints are marked deprecated

The [Obsolete] minimal API endpoint gets "deprecated": true in the .NET 11 document; on .NET 10 it didn’t.

No trailing slash on the server URL

.NET 10 wrote "url": "http://localhost:5410/"; .NET 11 writes "url": "http://localhost:5411" when there is no PathBase. Redocly’s no-server-trailing-slash error on the .NET 10 document disappears on .NET 11.

Your Generated Client Changes

This is the part that bites after the upgrade even when every tool “works”. I generated a C# client with NSwag 14.7.1 from each document. The method signatures:

== NSwag 14.7.1 client from openapi_net10.json
Task InvoiceAsync
Task<Order> OrdersAsync
Task<OrderCsvRow> ReportsAsync
Task<SseItemOfOrder> UpdatesAsync
== NSwag 14.7.1 client from openapi_net11.json
Task InvoiceAsync
Task<FileResponse> UpdatesAsync
Task<Order> OrdersAsync
Task<Order> ReportsAsync
  • ReportsAsync changes from Task<OrderCsvRow> to Task<Order>. The new one is correct (the action returns JSON by default), but callers that use the result as an OrderCsvRow stop compiling when you regenerate.
  • UpdatesAsync changes from Task<SseItemOfOrder> to Task<FileResponse>: NSwag doesn’t use itemSchema, so it treats the stream as raw content. The 3.1 document gives the same result.
  • The deprecated endpoint’s client methods get [System.Obsolete], which turns into CS0618 warnings in projects that use them (errors if you build with warnings as errors).

Kiota 1.36.1 generated a client from all three documents. For both .NET 11 documents it logged Could not create error type for 400 in (null), which is the new anyOf 400 response; the .NET 10 document gave no warning. openapi-typescript 7.13.0 wrote schema.d.ts for all three without messages. Redocly’s lint reported the same recommended-ruleset findings for all three (missing summary, no security), nothing about 3.2.

Upgrade Checklist

  • List everything that reads your openapi.json: client generators in other repositories, contract tests, gateways, API management imports, docs sites.
  • Anything on Microsoft.OpenApi 2.x: upgrade it to 3.x, or set OpenApiVersion = OpenApiSpecVersion.OpenApi3_1 on the API until it can.
  • Regenerate clients from the .NET 11 document before you release, and fix the callers: response types and SSE methods can change.
  • Check endpoints with several [ProducesResponseType] for the same status code; the document now shows all of them.
  • Expect CS0618 in clients generated from [Obsolete] endpoints.

Microsoft’s notes: What’s new in ASP.NET Core 11 (OpenAPI section) and Breaking changes in ASP.NET Core 11.

Related: Upgrading to .NET 11: 9 breaking changes · EF Core 11 breaking changes · API versioning in ASP.NET Core · all ASP.NET Core tutorials

Similar Posts

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *