Repository navigation
Description
Found by a pre-release sweep of release/2.3.1 @ dbdd44e plus #216, #217 and #218 as landed (lens: framework-heuristics); reproduced in a scratch OpenAPI test.
Package
CodoMetis.TypeKit.AspNetCore
Version
2.3.1 as above. Traced to 2.3.0 and not run there: the transformers have not changed since v2.3.0.
How does it fail?
Silently. The document describes a map or list of patches as admitting anything.
Problem
ASP.NET leaves out the element schema of a collection or dictionary whose element has its own converter, as Patch<T> does. Neither TypeKit transformer puts it back for a patch:
PatchSchemaTransformer.TransformAsyncacts only when the type itself is a patch (src/CodoMetis.TypeKit.AspNetCore/PatchSchemaTransformer.cs:92).- The container branch of
ValueObjectSchemaTransformerrestoresitems/additionalPropertiesfor a value-object element only (src/CodoMetis.TypeKit.AspNetCore/ValueObjectSchemaTransformer.cs:142-148).
The README and docs/merge-patches.md describe "a property typed Patch<T>", and neither names containers as a limit. A merge patch over a map ("a value sets the key, null clears it") is the shape this produces.
Scenario
public sealed record LabelsPatch(Dictionary<string, Patch<string>> Labels, List<Patch<int>> Quantities, Patch<string> Name);
app.MapPatch("/labels", int (LabelsPatch patch) => 0); // AddOpenApi(o => o.AddTypeKit())Measured on 2.3.1:
- OpenAPI 3.1:
labels: {"type":"object"},quantities: {"type":"array"}, whilename: {"type":["null","string"],…}. - OpenAPI 3.0: the same
labelsandquantities, andnamewith"nullable":true.
So a client generator types labels as Dictionary<string, object>.
Change
In the container branch, also handle an element that is a Patch<> (or a nullable one), and fill items/additionalProperties only where ASP.NET left them out. Use the argument's schema with null admitted (PatchSchemaTransformer.AdmitNull), which is what a Patch<T> property gets: a described or replaced argument, a value object inside the patch through its wrapped type, and OpenAPI 3.0's nullable form. If a Patch<T> element is not meant to be supported on the wire (an Absent element has no JSON), say so in the README and refuse it in the document rather than describe {}.
Test
Input class: the containers ASP.NET describes (List<>, arrays, Dictionary<string,>, IReadOnlyDictionary<,>) of Patch<T> over the PatchSchemaTests value catalogue, in OpenAPI 3.1, 3.0 and 2.0. The element schema accepts null and otherwise judges each value as the argument does. Reverted per verify-the-guard.
Environment
.NET SDK 10.0.4xx, Microsoft.AspNetCore.OpenApi 10; any consumer.
Found while fixing (#237)
- Elements are supported on the wire: a value sets a key or an element, and a null reads as the
Nullstate. An element is neverAbsenton read. So the element is described as a patch property is, in 3.1, 3.0 and 2.0, and nothing is rejected. - Sibling, same input class (
Found-By: VA): aPatch<T>?property was an empty, requiredPatchOfstringcomponent, and aList<Patch<T>?>had noitems. Both are now the argument's schema admitting null, without the merge-patch description. System.Text.Json reads anullinto aNullable<Patch<T>>asnull, not asNull, the same as a property left out. The docs say to usePatch<T>. - A host that inlines every schema (
CreateSchemaReferenceId = _ => null) can't describe a polymorphic argument at all. ASP.NET throws for a plain property of one too, so that's not TypeKit's.
Activity
docs: record KB-057, a nullable contract's element type and its null
CaffeinatedCoder commentedon Oct 31, 2026
Fixed by #237, merged into
release/2.3.1(cc72fa4); ships in 2.3.1.