Repository navigation
docs(downgrader): OpenAPI Generator Spring and FastAPI servers break on byte and null-only shapes - #239
Conversation
…list $ref limitation - progenitor reads an anyOf with two string-taking branches, the "null" branch counted as one, as a struct that takes only objects. - NSwag's undefined type for a $ref to a oneOf depends on its first branch. - $refs into the items or properties of a multi-type `type` list dangle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…ray/record gap - A $ref into the allOf of a schema that also holds a $ref resolves one member off, since that $ref becomes the first allOf member. - datamodel-code-generator, even with --strict-nullable, leaves null out of an array item or map value that is a nullable array or record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
openapi-zod-client 1.18 drops `nullable: true` from an anyOf branch, as a nullable tuple member or union member becomes; its bundled Swagger Parser rejects openapi 3.1.2, while 3.1.1 reads fine and keeps null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…y enum and byte getter note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…lable tuple member Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…ull, unions) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…m and byte branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…of z.null()
OpenAPI Generator's PHP client writes no allowed values for the enum of
an array item or map value that is { type: "null" } alone, which the
downgrader writes as { type: "string", nullable: true, enum: [null] },
so it throws on any such array or map that is not empty. Its 3.1 path
lets null through.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…4 arrays Kiota's TypeScript client reads every value of a oneOf of objects with the first branch. In 3.0 a const tag becomes a one-value enum, which the first branch rejects, so a value from any other branch of a Zod discriminated union loses its tag. It also reads an array of format: byte strings, which z.array(z.base64()) becomes, as one byte array and drops it; the C# client throws on such an array. From 3.1 Kiota keeps both as plain strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…e records It keeps null on a nullable record only as an object's property whose values reject null. As a component, an array item, a map value, a branch of an anyOf or oneOf, or with values that may be null, the record's type leaves out null. It reads the 3.1 anyOf with a null branch in all of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…le enums OpenAPI Generator 7.26 merges the oneOf a nullable enum becomes into one enum, and its go-server then checks that property or array item with an AssertstringRequired function it never writes. The existing normalizer flag avoids it. The 3.1 anyOf with a null branch compiles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
…on byte and null-only shapes The Spring server, like the Rust client, does not compile a required nullable format: byte property (Arrays.equals on a JsonNullable<byte[]>). It also writes the empty enum the Java clients write for type: null alone, and the Python FastAPI server writes `class X(, Enum)` for such a component. All of them compile from the 3.1 input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VVaw7SHxsMExLa1nyEQJVY
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
packages/downgrader/README.md— the two OpenAPI Generator limitation bullets now attribute the nullableformat: byterequired-property compile failure to thespringserver (with the optional-property caveat scoped to the Rust client), and attribute the null-only enum failure tospringandpython-fastapi(the latter scoped to a component).
Both additions were checked against OpenAPI Generator v7.26.0 source and both hold: the required nullable format: byte Spring model leaves the field as JsonNullable<byte[]> while pojo.mustache still emits Arrays.equals/Arrays.hashCode (the optional-nullable flag is unset), matching open issue #17935; and the null-only enum renders an empty enum body for spring/Java and class <Name>(, Enum): for a python-fastapi component. uncheck packages/downgrader/README.md passes.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Requested by dinwwwh · project thread
Summary
This adds two OpenAPI Generator 7.26 server generators to two existing README lines. Each one compiles from the 3.1 input but not from the downgraded 3.0.
springserver also fails to compile a required property that is a nullableformat: bytestring, such as Zod'sz.base64().nullable(). It keeps the value asJsonNullable<byte[]>, and itsequals/hashCodecallArrays.equals(JsonNullable<byte[]>, …), which matches no overload. An optional one compiles. The Rust-only clause about optional properties now says "In the Rust client".springserver writes the same empty enum the Java clients write for{ type: "null" }alone, in a property, an array item or a component. Thepython-fastapiserver writesclass Nul(, Enum):for such a component, which is aSyntaxError. Az.null()property or array item there is fine.How I found it
I compile-checked OpenAPI Generator's server generators on the oRPC Zod, Valibot and ArkType shape catalogs. Each was split into one doc per shape and generated from 3.1 and from the downgraded 3.0. I also ran python-fastapi end to end through FastAPI's
TestClienton 112 fuzz and shape docs.z.base64().nullable()no suitable method found for equals(JsonNullable<byte[]>, …)z.base64().nullable(), array of it, plainz.base64()z.null()property, array item, componentenum constant expected herez.null()component (property or array item)SyntaxErrorinclass Nul(, Enum):z.null()inline property or array itemOther shapes were clean. Once the null-only shapes are removed and safe-integer bounds get
format: int64,springcompiles every other catalog shape from 3.0. Without thatformat, Zod's ±2^53 bounds overflowintin both versions.python-fastapi's only other 3.0-only rejects are tuples, which 3.1 leaves untyped.OpenAPI Generator's
pythonclient writes the sameclass Nul(, Enum):. I left it out because it fails to generate from the 3.1 doc at all, so the 3.1 input is no way around it there.Test plan
pnpm exec uncheck packages/downgrader/README.mdpasses.Generated by Claude Code