OpenAPI schema generation marks a required property as nullable when its declared type is an open generic type parameter
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
## Is there an existing issue for this?
- [x] I have searched the existing issues (no matching issue found)
## Describe the bug
When a type has a `required` property whose declared type is an open generic
type parameter (e.g. `T Item` on `Envelope`), the OpenAPI schema generated
by `Microsoft.AspNetCore.OpenApi` (`AddOpenApi()`) marks that property's
value schema as nullable (`oneOf: [null, ref]`), even when:
- The property is `required` (so `item` correctly appears in the schema's
top-level `required` array), and
- The concrete type argument (`Widget` below) is a non-nullable reference
type, in a project with `enable`.
A `required` reference-typed property with a concrete (non-generic) type
does **not** show this problem in the same generated document — only
properties whose type is an open generic parameter do. This suggests the
underlying `NullabilityInfoContext` resolution doesn't correctly propagate
the closed generic's type-argument nullability through to the open generic
parameter's `PropertyInfo`, so the OpenAPI schema generator falls back to
treating it as nullable.
We noticed this because it makes API consumers (e.g. someone building a
request by hand in Postman from the spec) unsure whether the field is
genuinely optional — it isn't; the underlying property is `required` and
non-nullable in C#, and `AddValidation()` rejects an explicit `null` value at
runtime. The schema is just misleading about it.
## Expected Behavior
`EnvelopeOfWidget.item`'s schema should be a plain `$ref` to `Widget`
(non-nullable), matching how `Widget.name` (a concrete, non-generic
`required string`) is correctly emitted as `{ "type": "string" }` with no
null branch, in the very same document.
## Steps To Reproduce
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapPost("/widgets", (Envelope request) => Results.Ok(request.Item));
app.MapOpenApi();
app.Run();
public class Envelope
{
public required T Item { get; init; }
}
public class Widget
{
public required string Name { get; set; }
}
```
`GET /openapi/v1.json` produces, among other schemas:
```json
"EnvelopeOfWidget": {
"required": ["item"],
"type": "object",
"properties": {
"item": {
"oneOf": [
{ "type": "null" },
{ "$ref": "#/components/schemas/Widget" }
]
}
}
}
```
## Exceptions (if any)
None — this is a schema-correctness issue, not a crash.
## .NET Version
```
10.0.302
```
## Anything else?
- ASP.NET Core / `Microsoft.AspNetCore.OpenApi` version: 10.0.11
- OS: Ubuntu 24.04 (linux-arm64), via a dev container
- `dotnet --info` (Host): Version 10.0.10
Happy to submit a PR for this if a maintainer can point me at the right spot
in the nullability/schema-generation code.
Contributor guide
Research direction
Reproduce the issue with AddOpenApi(), MapOpenApi(), Envelope, and Widget, then inspect the Microsoft.AspNetCore.OpenApi nullability and schema-generation code, especially NullabilityInfoContext resolution for open generic parameters. Done means EnvelopeOfWidget.item is emitted as a non-nullable $ref to Widget without a null branch, while the property remains in the top-level required array.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, openapi
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 70/100