VersionedOpenApiOptionsFactory race: concurrent document requests cause NullReferenceException or unconfigured document
@commonsensesoftware is already working on this.
Since Sep 20, 2026.
Assessment
This issue has not been assessed yet.
Description
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
- Dominant language
- C#
- Stars
- 3.2k
- Forks
- 721
- PR merge metrics
- No merged PRs in 30d
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/aspnet-api-versioning
-
Stay backwards compatible within major versionPossibly taken @commonsensesoftware claimed this today. Openbug triage
dotnet/aspnet-api-versioning#1230 · 1 comment · 1 assignee ·
-
InvalidOperationException when create schema on transformerPossibly taken @commonsensesoftware claimed this 8 days ago. Openasp.net core triage
dotnet/aspnet-api-versioning#1227 · 1 assignee ·
-
.NET 11 RC - AOT Mode - System.NotSupportedException: ElementType is not initialized when `Microsoft.AspNetCore.Mvc.ApiExplorer.IsEnhancedModelMetadataSupported` is false.Possibly taken @commonsensesoftware claimed this 9 days ago. Openbug triage
dotnet/aspnet-api-versioning#1226 · 1 comment · 1 assignee ·
-
xmldoc file detection by assembly from a referenced projectPossibly taken @commonsensesoftware claimed this 23 days ago. Openasp.net core enhancement
dotnet/aspnet-api-versioning#1224 · 3 comments · 1 assignee ·
-
Feature request: declarative attribute for endpoints introduced in a specific API versionMay be free again @commonsensesoftware claimed this 53 days ago, and no pull request is open. Openasp.net core enhancement
dotnet/aspnet-api-versioning#1183 · 7 comments · 1 assignee ·
All issues in dotnet/aspnet-api-versioning
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
NethermindEth/nethermind#14012 ·
Maintainers usually reply within 1 day
-
dependencies Status: Triage
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
json-schema-org/website#2518 ·
Maintainers usually reply within 1 day
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
builtbybel/Flyoobe#498 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
builtbybel/CrapFixer#112 ·