Repository navigation
docs(downgrader): OpenAPI Generator rust-axum breaks on a nullable byte string with a pattern - #243
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
…te string with a pattern Zod's z.base64().nullable() becomes a nullable format: byte string that keeps its pattern. OpenAPI Generator 7.26's rust-axum server then checks the pattern with a function that takes a ByteArray, while the field is a Nullable<ByteArray>, so the property does not compile, required or optional. 3.1 input compiles. 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
- OpenAPI Generator
rust-axumnullable byte +patternnote — adds one clause to the existing OAG bullet inpackages/downgrader/README.md: in 7.26 the Rust server generator (rust-axum) does not compile a nullableformat: byteproperty, required or optional, that also carries apattern, as Zod'sz.base64()writes.
I checked the load-bearing claim against OAG v7.26.0: in rust-axum/models.mustache an isByteArray property with a pattern emits custom(function = "validate_byte_<class>_<prop>"), whose signature is fn(&ByteArray), while the field is typed Nullable<ByteArray> (required) or Option<Nullable<ByteArray>> (optional) — and ModelUtils.isByteArraySchema matches format: byte, so the branch is reached. The custom validation is emitted outside the {{^isNullable}} guard (unlike the XSS checks), which is exactly the reported E0308. Since the PR's own table shows non-null optional byte + pattern compiles, validator unwraps Option and passes the inner type, so the Nullable<ByteArray> case is what mismatches. The Zod-writes-a-pattern premise is also already documented in the repo's ogen note. uncheck passes for the file.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Requested by dinwwwh · project thread
Summary
Zod's
z.base64().nullable()writesanyOf: [{ type: "string", contentEncoding: "base64", pattern: "..." }, { type: "null" }]. The downgrader turns it into{ type: "string", format: "byte", pattern: "...", nullable: true }, as designed.OpenAPI Generator 7.26's
rust-axumserver checks aformat: bytepattern with a generatedvalidate_byte_*function that takes&ByteArray. A nullable property's field is aNullable<ByteArray>, so the model does not compile:This adds one sentence to the existing note on OpenAPI Generator's Rust client and Spring server.
What was checked
Each shape was compiled with
cargo buildfrom both the 3.1 input and the downgraded 3.0 output:Only README text changes.
uncheckpasses.Generated by Claude Code