VersionedOpenApiOptionsFactory race: concurrent document requests cause NullReferenceException or unconfigured document
@commonsensesoftware ci sta già lavorando.
Dal 20/9/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
VersionedOpenApiOptionsFactory is registered as a singleton but keeps the context of the options being created in an instance field (private Context? context).
CreateAndConfigure sets it, calls Create, then clears it.
If the options for two documents are created at the same time, one thread can clear the field between another thread's null check and its use.
Expected Behavior
Every document is built with its own versioned configuration regardless of how many documents are requested concurrently.
Steps To Reproduce
Minimal project (two files). It starts a fresh host 20 times and requests /openapi/1.json and /openapi/2.json at the same moment. A run counts as failed if either response is not 200 or has no versioned paths.
Repro.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
<PackageReference Include="Asp.Versioning.OpenApi" Version="10.2.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
</ItemGroup>
</Project>
Program.cs
using Asp.Versioning;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
// Starts the app repeatedly and requests both OpenAPI documents at the same moment.
var failures = 0;
const int runs = 20;
for (var i = 0; i < runs; i++)
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseUrls("http://127.0.0.1:0");
builder.Logging.ClearProviders();
builder.Services.AddControllers();
builder.Services.AddApiVersioning()
.AddMvc()
.AddApiExplorer(o => o.GroupNameFormat = "VVV")
.AddOpenApi();
var app = builder.Build();
app.UseExceptionHandler(errorApp => errorApp.Run(context =>
context.Response.WriteAsync(context.Features.Get<IExceptionHandlerFeature>()?.Error.ToString() ?? string.Empty)));
app.MapControllers();
app.MapOpenApi("/openapi/{documentName}.json").WithDocumentPerVersion();
await app.StartAsync();
using var client = new HttpClient { BaseAddress = new Uri(app.Urls.First()) };
var gate = new TaskCompletionSource();
async Task<(bool Ok, string Text)> Get(string name)
{
await gate.Task;
var response = await client.GetAsync($"/openapi/{name}.json");
var body = await response.Content.ReadAsStringAsync();
// a correctly configured document lists the versioned paths; an unconfigured one has none
var ok = response.IsSuccessStatusCode && body.Contains("/api/v");
var text = $"{name}: {(int)response.StatusCode} length={body.Length} hasPaths={body.Contains("/api/v")}";
return (ok, response.IsSuccessStatusCode ? text : text + Environment.NewLine + string.Join(Environment.NewLine, body.Split('\n').Take(4)));
}
var v1 = Get("1");
var v2 = Get("2");
gate.SetResult();
var results = await Task.WhenAll(v1, v2);
var ok = results.All(r => r.Ok);
if (!ok) failures++;
Console.WriteLine($"run {i,2}: {(ok ? "ok " : "FAIL")} {string.Join(" | ", results.Select(r => r.Text))}");
await app.StopAsync();
await app.DisposeAsync();
}
Console.WriteLine($"{failures} of {runs} runs failed");
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/values")]
public class ValuesV1Controller : ControllerBase
{
[HttpGet]
public string Get() => "v1";
}
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/values")]
public class ValuesV2Controller : ControllerBase
{
[HttpGet]
public string Get() => "v2";
}
dotnet run -c Release
Result: between 1 and 4 of 20 runs fail per attempt, usually including the first (cold) run.
Exceptions (if any)
System.NullReferenceException: Object reference not set to an instance of an object.
at Asp.Versioning.OpenApi.Configuration.VersionedOpenApiOptionsFactory.Create(String name)
at Asp.Versioning.OpenApi.Configuration.VersionedOpenApiOptionsFactory.CreateAndConfigure(Context newContext)
at Asp.Versioning.OpenApi.Configuration.ConfigureOpenApiOptions.PostConfigure(String name, OpenApiOptions options)
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at Microsoft.Extensions.Options.OptionsCache`1.GetOrAdd[TArg](String name, Func`3 createOptions, TArg factoryArgument)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService..ctor(...)
at Asp.Versioning.OpenApi.Reflection.Class.OpenApiDocumentService.OpenApiDocumentServiceCtor(...)
.NET Version
10.0.401
Anything else?
- ASP.NET Core version: 10.0.12
- Asp.Versioning.OpenApi 10.2.2 (the factory on main is unchanged: the mutable
contextfield is still there) - OS: Windows 11
- Lingua principale
- C#
- Stelle
- 3.2k
- Fork
- 721
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di dotnet/aspnet-api-versioning
-
Stay backwards compatible within major versionForse già presa @commonsensesoftware l’ha presa 1 giorno fa. Apertabug triage
dotnet/aspnet-api-versioning#1230 · 1 commento · 1 assegnatario ·
-
InvalidOperationException when create schema on transformerForse già presa @commonsensesoftware l’ha presa 9 giorni fa. Apertaasp.net core triage
dotnet/aspnet-api-versioning#1227 · 1 assegnatario ·
-
.NET 11 RC - AOT Mode - System.NotSupportedException: ElementType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false.Forse già presa @commonsensesoftware l’ha presa 9 giorni fa. Apertabug triage
dotnet/aspnet-api-versioning#1226 · 1 commento · 1 assegnatario ·
-
xmldoc file detection by assembly from a referenced projectForse già presa @commonsensesoftware l’ha presa 24 giorni fa. Apertaasp.net core enhancement
dotnet/aspnet-api-versioning#1224 · 3 commenti · 1 assegnatario ·
-
Feature request: declarative attribute for endpoints introduced in a specific API versionForse di nuovo libera @commonsensesoftware l’ha presa 53 giorni fa e non c’è nessuna pull request aperta. Apertaasp.net core enhancement
dotnet/aspnet-api-versioning#1183 · 7 commenti · 1 assegnatario ·
Tutte le issue di dotnet/aspnet-api-versioning
Issue simili
-
agentic-workflows area/Docs partner/agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
microsoft/fluentui-blazor#5364 ·
I maintainer di solito rispondono entro 1 giorno
-
.NET triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
microsoft/agent-framework#8811 ·
I maintainer di solito rispondono entro 1 giorno
-
.NET Docs
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
getsentry/sentry-dotnet#5637 · 1 commento ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
QuantConnect/Lean#9842 ·
I maintainer di solito rispondono entro 1 giorno