Repository navigation
OpenAPI 3.1 V31 deserializer loses nullable when "nullable" key appears before "type" (key-order sensitive) #2845
Description
[Bug] OpenAPI 3.1 V31 deserializer loses nullable when "nullable" key appears before "type" (key-order sensitive)
Describe the bug
When parsing an OpenAPI 3.1 spec, the V31 deserializer silently loses nullability for properties that use nullable: true if the nullable key appears before type in the JSON object. The result is that nullable: true is ignored and the property is treated as non-nullable.
This is a key-ordering bug: the "nullable" handler sets o.Type = JsonSchemaType.Null when o.Type is not yet defined, but the "type" handler then does a direct assignment o.Type = parsedType, overwriting the Null flag. If the keys appear in the opposite order (type before nullable), the handler correctly does o.Type |= JsonSchemaType.Null and the nullability is preserved.
This does not affect OpenAPI 3.0 specs the V30 deserializer handles nullable as a post-processing step after all keys are read, making it order-independent.
Note on spec validity:
nullable: trueis technically an OpenAPI 3.0 construct and does not exist in the OpenAPI 3.1 / JSON Schema 2020-12 vocabulary (where nullability is expressed as"type": ["string", "null"]). However, since the library explicitly supportsnullablein 3.1 for backwards compatibility, it should do so consistently regardless of key ordering.
OpenApi File To Reproduce
Minimal reproducer (note: nullable appears before type):
{
"openapi": "3.1.0",
"info": { "title": "Test", "version": "1.0" },
"components": {
"schemas": {
"MyObject": {
"type": "object",
"required": ["system_fingerprint"],
"properties": {
"system_fingerprint": {
"description": "System fingerprint",
"nullable": true,
"type": "string"
}
}
}
}
}
}Steps to reproduce:
- Load the schema above with
OpenApiDocument.LoadAsync - Inspect
components.schemas.MyObject.properties.system_fingerprint - Observe that
Schema.TypeisString(16) and does not includeNull
Expected behavior
system_fingerprint should be parsed as nullable Schema.Type should be Null | String (17) regardless of whether nullable or type appears first in the JSON object.
Screenshots / Code Snippets
Tested on Microsoft.OpenApi 3.5.3. Results:
| Key order | OpenAPI version | Schema.Type |
Nullable |
|---|---|---|---|
nullable before type |
3.1 | String (16) |
❌ |
type before nullable |
3.1 | Null | String (17) |
✅ |
nullable before type |
3.0 | Null | String (17) |
✅ |
type before nullable |
3.0 | Null | String (17) |
✅ |
The root cause is in OpenApiV31Deserializer. The "type" handler overwrites o.Type directly without preserving an existing Null flag set by a preceding "nullable": true:
// Current behavior — overwrites any previously set Null flag
"type", (o, n, _) => o.Type = n.GetScalarValue()?.ToJsonSchemaType()A minimal fix would be to preserve the Null flag if already set:
"type", (o, n, _) =>
{
var parsed = n.GetScalarValue()?.ToJsonSchemaType();
o.Type = (o.Type.HasValue && o.Type.Value.HasFlag(JsonSchemaType.Null))
? parsed | JsonSchemaType.Null
: parsed;
}Additional context
- Affects Microsoft.OpenApi up to and including 3.5.3 (v3.5.3 only addresses boolean schema handling)
- The V30 deserializer is not affected because it stores
nullableas a deferred extension and applies it after all keys are parsed - As a workaround, pre-normalizing the JSON document by rewriting
{ "nullable": true, "type": "X" }to{ "type": ["X", "null"] }before callingLoadAsyncproduces the correct result
Activity
baywet commentedon May 19, 2026
Hi jrabau
Thank you for using the SDK and for reaching out.
Is this something you'd like to submit a pull request for provided some guidance?
microsoft-github-policy-service commentedon May 25, 2026
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.