Skip to content

[JAVA-SPRING] Convert byte[] to String for operation params [QueryParam; PathParam; HeaderParam; CookieParam; FormParam] - #22930

Draft
Picazsoo wants to merge 39 commits into
OpenAPITools:masterfrom
Picazsoo:feature/java-spring-convert-byte-array-operation-params-to-String
Draft

Picazsoo wants to merge 39 commits into
OpenAPITools:masterfrom
Picazsoo:feature/java-spring-convert-byte-array-operation-params-to-String

Conversation

@Picazsoo

@Picazsoo Picazsoo commented Feb 9, 2026 •

Copy link
Copy Markdown
Contributor

Correctly mapping OpenAPI format: byte for the Java Spring generator

Fixes: #22898
Scope: SpringCodegen.java (Java client/server) — spring-boot, spring-cloud (Feign), spring-http-interface libraries. kotlin-spring is not covered (see "Known gaps" below).


1. The problem

The OpenAPI Specification defines type: string, format: byte as "base64 encoded characters" — i.e. the wire value is always a base64 string, and it is the application's job to decode/encode it to actual bytes:

byte – base64 encoded characters
— OpenAPI/Swagger data types reference

Before this fix, the Java Spring generator mapped format: byte to byte[] everywhere a byte-format value could appear: in the JSON body, and also in query, path, header, cookie, and form parameters. That mapping is only actually correct for one of those locations.

Why byte[] works for a JSON request/response body

When format: byte appears on a DTO field serialized by Jackson (the JSON body case), Jackson's ObjectMapper automatically base64-encodes/decodes byte[] fields using its default Base64Variant (MIME_NO_LINEFEEDS). This is standard, well-documented Jackson behavior — see the Jackson databind Javadoc for binary/byte[] handling and Base64Variants. So byte[] in a JSON body is genuinely correct and needs no change — the framework does the base64 work for you.

Why byte[] is wrong for query/path/header/cookie/form params

Query, path, header, cookie, and form-urlencoded parameters are not deserialized by Jackson at all. Spring MVC/WebFlux binds these using its general-purpose ConversionService (@RequestParam, @PathVariable, @RequestHeader, @CookieValue — see Spring's type conversion documentation and the @RequestParam reference).

I decompiled spring-core-6.2.18.jar to see exactly what the default ConversionService does with a byte[] target/source, and confirmed the registered converter is org.springframework.core.convert.support.ArrayToStringConverter (and its inverse, StringToArrayConverter). Its logic is:

ArrayToStringConverter.convert(byte[] source, ...)
  → ObjectUtils.toObjectArray(source)      // boxes each byte to Byte
  → Arrays.asList(...)
  → CollectionToStringConverter            // joins elements with "," using Object#toString()

So a byte array such as {104, 101, 108, 108, 111} ("hello") is not base64-encoded by this path — it is rendered as the literal string "104,101,108,108,111" (decimal byte values joined by commas). The reverse conversion (StringToArrayConverter, incoming request → controller method param) expects that same comma-separated decimal format, not base64.

Consequence of the old behavior: any client sending a real base64 string (as the OpenAPI spec requires) into a query/path/header/cookie/form parameter typed byte[] would fail to bind correctly (Spring would try to parse the base64 string as comma-separated integers and typically throw a conversion exception), and any server emitting a byte[] into one of these locations would not actually produce valid base64 on the wire. The byte[] type was misleading — it implied "correctly typed bytes," but no component in the request pipeline for these locations understood base64.


2. The fix

2.1 convertByteArrayParamsToStringType(operation)

For every parameter on an operation whose location is not the request body (queryParams, pathParams, headerParams, cookieParams, formParams) and whose OpenAPI type is string, format: byte, the generator now rewrites the generated Java dataType from byte[] to String. A short code comment (/* base64 encoded binary */) is emitted next to the parameter in the generated method signature so the base64 contract is visible to anyone reading the generated code.

This means:

  • Consumers of a generated client (Feign, spring-http-interface, RestTemplate/WebClient-based clients) must now pass an already-base64-encoded String, e.g. Base64.getEncoder().encodeToString(bytes), instead of a raw byte[].
  • Implementers of a generated server controller receive the base64 String as-is and must decode it themselves, e.g. Base64.getDecoder().decode(value), when they need the actual bytes.

This is intentionally symmetrical with what a hand-written Spring application would have to do anyway, since Spring's ConversionService was never going to do this for you correctly.

2.2 markMultipartFormDataParameters(operation) (reactive/WebFlux only)

Spring WebFlux does not support the same MVC-style binding for multipart/form-data text fields — WebFlux typically needs @RequestPart (bound to a Part/Mono<Part>) for all parts of a multipart request, including plain text fields, whereas Spring MVC can bind simple text fields with a plain @RequestParam. This method flags each non-file multipart field with the vendor extension x-isMultipartFormData so the mustache templates can:

  • emit @RequestPart (not @RequestParam) for reactive text fields, and
  • type them as String (or Flux<String>/List<String> for repeated/array fields) rather than the raw model type, consistent with how WebFlux exposes multipart text parts.

File parts (format: binary) are unaffected by either method and keep the framework's native multipart abstraction — MultipartFile for Spring MVC, Part/FilePart for WebFlux — since these already stream raw bytes correctly with no base64 involved.

2.3 Summary of the resulting type mapping

Location OpenAPI type Generated Java type Encoding handled by
Query / Path / Header / Cookie / Form string, format: byte String Manual (caller/implementer does base64 encode/decode)
Multipart text field string, format: byte or plain string String Manual, same as above
Multipart file field string, format: binary MultipartFile (MVC) / Part (WebFlux) None needed — raw bytes stream through the multipart part
JSON request/response body field string, format: byte byte[] Automatic — Jackson base64-encodes/decodes by default
Raw application/octet-stream body string, format: binary Resource None needed — streamed, no decoding

This preserves the important distinction the OpenAPI spec makes between format: byte (base64 text) and format: binary (raw bytes), rather than collapsing both into byte[] regardless of transport location.


3. Why this is correct (not just "different")

  1. The old byte[] typing for params was already broken, not merely stylistic. See the decompiled ArrayToStringConverter behavior in §1 — Spring's default conversion never implemented base64 for these locations, so a byte[] in a query/path/header/cookie/form parameter round-tripped through decimal-CSV, not base64. This is confirmed by direct bytecode inspection of spring-core, not conjecture.
  2. This mirrors the framework's own type boundary. Spring's Jackson-backed HttpMessageConverter path (JSON body) is the only place in the stack that base64-encodes byte[] automatically. Everywhere else, Spring intentionally leaves conversion to whatever Converter<S,T> is registered, and none is registered for base64. Typing the generated parameter as String accurately reflects this framework boundary instead of hiding it behind a byte[] that silently didn't work.
  3. It is a breaking, but necessary, change to generated client method signatures. Any hand-written caller previously passing a byte[] to a generated Feign/http-interface/WebClient method for one of these locations must now pass a base64 String. This is source-incompatible, but the previous behavior was not actually functioning end-to-end, so there is no working call pattern being broken — only incorrect code that either never compiled against real usage or silently sent corrupt data.

4. Verification performed (this session)

4.1 Unit / codegen tests

  • SpringCodegenTest.shouldHandleFormatByteCorrectlyForAllApiParametersAndProperties (drives byte-format-baseline.yaml) — asserts the exact type mapping table in §2.3 for every param location and for JSON/binary bodies.
  • SpringCodegenTest.testMultipartBoot / testReactiveMultipartBoot, JavaClientCodegenTest multipart tests, KotlinSpringServerCodegenTest — all passing after the master merge (953 targeted tests, 0 failures).

4.2 Real generated sample projects (compiled + integration-tested)

  • samples/server/petstore/springboot-byte-format-edge-cases (Spring MVC, blocking) — 21 integration tests (MockMvc), 0 failures.
  • samples/server/petstore/springboot-byte-format-edge-cases-reactive (WebFlux) — 22 integration tests (WebTestClient), 0 failures.
  • Both projects have a real hand-written controller implementation (CoverageApiTestControllerImpl) that base64-decodes incoming values and compares against known expected byte content, so the tests validate actual data correctness, not just type-compatibility.

4.3 Live end-to-end curl verification (both MVC and WebFlux servers actually running)

Ran both sample apps live (mvn spring-boot:run, blocking on Tomcat and reactive on Netty) and exercised every location by hand:

  • Query, path, header, cookie, form params: valid base64 → 204; base64 of the wrong content → 500 (proves the server genuinely decodes and content-checks, not just accepts any string).
  • Multipart simple (file + base64 text field) and multipart mixed (including repeated statusArray via @RequestParam and JSON markerArray via @RequestPart) → 204 for both blocking and reactive.
  • Raw application/octet-stream body: correct bytes → 204; wrong bytes → 500.
  • Results were identical between blocking and reactive modes, confirming the WebFlux-specific @RequestPart/Flux<String> handling in markMultipartFormDataParameters works correctly, not just compiles.

4.4 Client-side libraries (Feign / spring-http-interface)

Regenerated byte-format-baseline.yaml with --library spring-cloud and --library spring-http-interface via the built CLI jar:

  • Confirmed byte-format params (query/path/header/cookie/form) are generated as String with the base64 comment, exactly as in the spring-boot server case.
  • Confirmed multipart file parameters remain MultipartFile with @RequestPart, and text fields become @RequestParam String.
  • Both generated clients compiled cleanly (mvn compile, zero errors) — the change does not break Feign contract parsing or the Spring 6 @HttpExchange-based declarative HTTP interface generation. Both share the exact same SpringCodegen.postProcessOperationsWithModels() code path as spring-boot, so there is no separate logic path that could diverge.

4.5 Known gap identified (not fixed by this branch)

kotlin-spring uses a separate codegen class, KotlinSpringServerCodegen (extends AbstractKotlinCodegen, not SpringCodegen), with its own template set. Regenerating the same spec with -g kotlin-spring confirmed the query parameter is still generated as bytes: kotlin.ByteArray? — i.e. the same underlying bug (§1) still exists there. Porting this fix to Kotlin would be a separate, follow-up change; it is out of scope for this PR/branch.


5. References

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request. - tagging: @cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @Zomzog (2022/09) @martin-mfg (2023/08)

Summary by cubic

Converts byte[] operation parameters to String in the Java Spring generator for query, path, header, cookie, and form to fix base64 handling and align generated types with the OpenAPI spec. Also fixes multipart binding edge cases and adds MVC/WebFlux samples and tests to cover them.

Bug Fixes

  • Map byte and array-of-byte params to String/List<String> in SpringCodegen; add the base64 comment via lambdaTypeComment and a "(base64 encoded)" note in the shared @param Javadoc.
  • Preserve types elsewhere: byte[] in models and JSON bodies, Resource for application/octet-stream, MultipartFile/Part for multipart files; reactive non-file form params now use @RequestParam.
  • Fix mixed multipart arrays and the multipart media-type check in Java Spring, adding CoverageApi endpoints and new MVC/WebFlux edge-case samples with CI generation.
  • RFC3339DateFormat forces UTC, uses a per-call StdDateFormat, and its clone() returns a proper copy; reactive sample controllers close request streams and release pooled DataBuffer instances.
  • Isolate the new array and mixed-case multipart coverage in a dedicated form-multipart-binary-array-extended.yaml spec so the shared fixture and C# restsharp client remain unchanged.

Migration

  • If you previously used byte[] for these params, decode the provided base64 String yourself (e.g., Base64.getDecoder().decode(value)).

Written for commit 53e80e5. Summary will update on new commits.

Review in cubic

@Picazsoo
Picazsoo marked this pull request as ready for review February 9, 2026 22:51

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 41 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java:383">
P2: @RequestPart is for multipart/form-data, but this endpoint consumes application/x-www-form-urlencoded. The modified parameter still uses @RequestPart, so WebFlux won’t bind the form body correctly (likely 400).</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java:944">
P2: Form/multipart DTO still asserts `format: byte` as `byte[]`, but Spring’s default data binding does not Base64‑decode form strings to `byte[]`; it converts the string to raw bytes. This makes form DTO binding inconsistent with the new `String` parameter handling and can yield incorrect values without a custom converter.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 33 files (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java:16">
P2: RFC3339DateFormat hardcodes the delegate StdDateFormat to UTC and does not override setTimeZone, so any configured timezone will be ignored and dates will always format in UTC.</violation>

<violation number="2" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java:36">
P2: clone() returns the same instance instead of a copy, violating the Format/DateFormat clone contract and preventing callers from obtaining an independent DateFormat instance.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Picazsoo
Picazsoo marked this pull request as draft February 10, 2026 09:38
@Picazsoo
Picazsoo marked this pull request as ready for review February 11, 2026 15:36

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found across 87 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs">

<violation number="1" location="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs:156">
P2: MarkerArray is compared by contents via CompareNetObjects, but GetHashCode uses List<T>.GetHashCode() (reference-based), violating the Equals/GetHashCode contract for equal instances with different list instances.</violation>
</file>

<file name="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Api/MultipartApi.cs">

<violation number="1" location="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Api/MultipartApi.cs:526">
P2: markerArray is serialized with ParameterToString for multipart/form-data, which turns a list of complex objects into a comma-separated ToString output rather than JSON. This likely produces an invalid multipart part for an array of objects; use JSON serialization instead (matching the async method).</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/api/PathApi.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/api/PathApi.java:65">
P2: Converted byte-format parameter is now a String without preserving `format: byte` in the @Parameter schema, which causes generated OpenAPI docs/clients to lose the Base64 byte semantics.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java:16">
P2: RFC3339DateFormat extends DateFormat but ignores DateFormat configuration and returns `this` from clone(). Callers that set time zone/leniency will only update the inherited calendar, while the delegate `fmt` stays locked to UTC; and cloning returns a shared mutable instance, breaking expected DateFormat clone semantics and risking shared state across threads.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java:309">
P2: @RequestParam in WebFlux only binds query parameters; using it for application/x-www-form-urlencoded form fields breaks binding of request-body form data.</violation>

<violation number="2" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java:351">
P2: Multipart form fields in WebFlux are not bound by @RequestParam; using it for multipart/form-data will ignore request-body fields.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java:666">
P2: @RequestParam in WebFlux binds only query parameters; using it for multipart/form-data fields (additionalMetadata) prevents binding from the request body.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java:316">
P2: In WebFlux, @RequestParam binds query parameters only; with application/x-www-form-urlencoded this will ignore form body fields, so name/status won’t bind for typical POST form submissions.</violation>

<violation number="2" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java:359">
P2: @RequestParam in WebFlux binds only query parameters, so additionalMetadata will no longer be read from multipart/form-data. This regresses multipart uploads and should remain @RequestPart.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Picazsoo
Picazsoo marked this pull request as draft February 11, 2026 16:05
@Picazsoo Picazsoo closed this Feb 11, 2026
@Picazsoo

Copy link
Copy Markdown
Contributor Author

Hello @cubic-dev-ai, please rerun the CR. And take into accounts all the passing integration tests I added (they indeed all pass)

@cubic-dev-ai

cubic-dev-ai Bot commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Hello @cubic-dev-ai, please rerun the CR. And take into accounts all the passing integration tests I added (they indeed all pass)

@Picazsoo I have started the AI code review. It will take a few minutes to complete.

@Picazsoo
Picazsoo marked this pull request as ready for review February 12, 2026 17:58

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 issues found across 97 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java:36">
P2: clone() returns the same instance, violating DateFormat’s clone contract and preventing per-thread/per-request clones, which can lead to shared mutable state and ignored configuration in Jackson.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java:428">
P3: Form parameter enum_form_string_array declares a default in @ApiParam but @RequestParam omits defaultValue, so the default isn’t applied and missing values resolve to null.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java:316">
P2: @RequestParam in Spring WebFlux only binds query parameters, so form-urlencoded body fields won’t populate name/status here. With consumes=application/x-www-form-urlencoded, this change can lead to null form values. Use form-data binding via @ModelAttribute or read ServerWebExchange.getFormData()/@RequestBody MultiValueMap instead.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/ApiUtil.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/ApiUtil.java:16">
P2: Response body is always encoded as UTF-8 even when the provided MediaType specifies a different charset, which can cause clients to decode the body incorrectly.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/RFC3339DateFormat.java:36">
P2: clone() returns `this`, violating the clone contract for mutable DateFormat subclasses and causing callers to share a single mutable instance instead of getting an independent copy.</violation>
</file>

<file name="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs">

<violation number="1" location="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs:156">
P2: GetHashCode uses List.GetHashCode for MarkerArray while Equals performs deep comparison, breaking the equality contract for objects with equivalent list contents.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java:380">
P2: @RequestParam in WebFlux only binds query parameters; for application/x-www-form-urlencoded POST bodies these required form fields will not bind, leading to missing-parameter 400s.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/configuration/SpringDocConfiguration.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/configuration/SpringDocConfiguration.java:21">
P3: API title in generated SpringDoc config is HTML-escaped ("&amp;") and no longer matches the source OpenAPI spec, leading to an incorrect title in the generated OpenAPI/Swagger UI output.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java:72">
P2: The InputStream from `resource.getInputStream()` is never closed and the blocking `readAllBytes` runs on the subscriber thread (no boundedElastic scheduler). This can leak file descriptors and block the Netty event loop in WebFlux.</violation>

<violation number="2" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java:194">
P2: DataBuffer instances from multipart Part content are consumed but never released, which can leak pooled Netty buffers under load. Release buffers after reading (DataBufferUtils.release) or use releaseConsumer to avoid native memory leaks.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java:309">
P2: In Spring WebFlux, @RequestParam only binds query parameters, so using it for application/x-www-form-urlencoded body fields will leave name/status null for typical form submissions. This breaks the form update endpoint.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(mediaType);

byte[] exampleBytes = example.getBytes(StandardCharsets.UTF_8);

@cubic-dev-ai cubic-dev-ai Bot Feb 12, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Response body is always encoded as UTF-8 even when the provided MediaType specifies a different charset, which can cause clients to decode the body incorrectly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/ApiUtil.java, line 16:

<comment>Response body is always encoded as UTF-8 even when the provided MediaType specifies a different charset, which can cause clients to decode the body incorrectly.</comment>

<file context>
@@ -0,0 +1,20 @@
+        ServerHttpResponse response = exchange.getResponse();
+        response.getHeaders().setContentType(mediaType);
+
+        byte[] exampleBytes = example.getBytes(StandardCharsets.UTF_8);
+        DefaultDataBuffer data = new DefaultDataBufferFactory().wrap(exampleBytes);
+        return response.writeWith(Mono.just(data));
</file context>
Fix with Cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 97 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/configuration/SpringDocConfiguration.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/configuration/SpringDocConfiguration.java:21">
P3: OpenAPI title is HTML-escaped in a Java string literal, so the title will render as "&amp;" instead of "&".</violation>
</file>

<file name="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs">

<violation number="1" location="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Model/MultipartMixedRequest.cs:156">
P2: GetHashCode uses List<T>.GetHashCode() for MarkerArray, which is reference-based, while Equals performs deep comparison via Compare.NET Objects. This breaks the equality/hash code contract for equal instances with equivalent list contents and can misbehave in Dictionary/HashSet.</violation>
</file>

<file name="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java">

<violation number="1" location="samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java:316">
P2: In Spring WebFlux, @RequestParam only binds query parameters. For application/x-www-form-urlencoded POSTs, these parameters come from the request body, so name/status will not bind and will be null. Use @ModelAttribute or @RequestBody form data binding instead.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/RFC3339DateFormat.java:36">
P2: RFC3339DateFormat.clone() returns the same mutable instance, violating the DateFormat/Cloneable contract and causing shared state when callers clone the formatter (e.g., for thread safety).</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java:51">
P2: Unclosed InputStream and blocking I/O in `Mono.fromCallable` can leak resources and block the WebFlux event loop. Use try-with-resources to close the stream and offload blocking reads to `Schedulers.boundedElastic()`.</violation>

<violation number="2" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java:194">
P2: Reactive multipart handlers consume DataBuffer instances without releasing them, which can leak pooled buffers in WebFlux.</violation>
</file>

<file name="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Api/MultipartApi.cs">

<violation number="1" location="samples/client/others/csharp-complex-files/src/Org.OpenAPITools/Api/MultipartApi.cs:526">
P2: markerArray is serialized with ParameterToString in multipart requests, which calls ToString on each complex object and produces a debug string rather than JSON. This likely sends invalid data for a list of MultipartMixedRequestMarker objects.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/RFC3339DateFormat.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/RFC3339DateFormat.java:36">
P2: clone() returns `this`, breaking the DateFormat clone contract and causing shared mutable state; this defeats thread-safe use of DateFormat clones.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}

private Mono<Void> verifyBase64Content(String base64Value, String fieldName) {
try {

@cubic-dev-ai cubic-dev-ai Bot Feb 12, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unclosed InputStream and blocking I/O in Mono.fromCallable can leak resources and block the WebFlux event loop. Use try-with-resources to close the stream and offload blocking reads to Schedulers.boundedElastic().

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-byte-format-edge-cases-reactive/src/main/java/org/openapitools/api/CoverageApiTestControllerImpl.java, line 51:

<comment>Unclosed InputStream and blocking I/O in `Mono.fromCallable` can leak resources and block the WebFlux event loop. Use try-with-resources to close the stream and offload blocking reads to `Schedulers.boundedElastic()`.</comment>

<file context>
@@ -0,0 +1,415 @@
+    }
+
+    private Mono<Void> verifyBase64Content(String base64Value, String fieldName) {
+        try {
+            byte[] decoded = Base64.getDecoder().decode(base64Value);
+            logger.debug("Base64 decoded successfully for field: {}", fieldName);
</file context>
Fix with Cubic

Picazsoo and others added 3 commits June 22, 2026 15:13
…ration-params-to-String

# Conflicts:
#	.github/workflows/samples-spring.yaml
#	modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustache
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
#	samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-beanvalidation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-builtin-validation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-delegate-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-delegate/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-implicitHeaders/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/PetApi.java
#	samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/PetApi.java
#	samples/server/petstore/springboot-spring-pageable-delegatePattern-without-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable-delegatePattern/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable-without-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-useoptional/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-x-implements-skip/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot/src/main/java/org/openapitools/api/FakeApi.java
…-convert-byte-array-operation-params-to-String

# Conflicts:
#	modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java
#	modules/openapi-generator/src/main/resources/JavaSpring/formParams.mustache
#	modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java
#	samples/client/petstore/spring-http-interface-bean-validation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/client/petstore/spring-http-interface-reactive-bean-validation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/client/petstore/spring-http-interface-springboot-4/src/main/java/org/openapitools/api/FakeApi.java
#	samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/api/FakeApi.java
#	samples/openapi3/client/petstore/spring-cloud-oas3-fakeapi/src/main/java/org/openapitools/api/FakeApi.java
#	samples/openapi3/server/petstore/springboot-delegate/src/main/java/org/openapitools/api/FakeApi.java
#	samples/openapi3/server/petstore/springboot-implicitHeaders/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/spring-boot-defaultInterface-unhandledExcp/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-beanvalidation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-builtin-validation/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-delegate-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-delegate/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-implicitHeaders/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-include-http-request-context/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-reactive/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable-delegatePattern-without-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable-delegatePattern/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable-without-j8/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-spring-pageable/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-useoptional/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot-virtualan/src/main/java/org/openapitools/virtualan/api/FakeApi.java
#	samples/server/petstore/springboot-x-implements-skip/src/main/java/org/openapitools/api/FakeApi.java
#	samples/server/petstore/springboot/src/main/java/org/openapitools/api/FakeApi.java
@Picazsoo
Picazsoo marked this pull request as ready for review September 27, 2026 10:36

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 existing issue remains and 3 new issues found across 104 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/server/petstore/springboot-byte-format-edge-cases/README.md">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/README.md:16">
P3: These examples cannot compile against this sample because it exposes `CoverageApi`, not `PetApi`. Update the examples to use the sample's actual API or remove the unrelated Petstore boilerplate.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/pom.xml">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/pom.xml:27">
P2: This build omits `spring-boot-maven-plugin`, so `mvn package` produces a plain JAR instead of a launchable Spring Boot archive. Add the plugin as in the existing Spring Boot sample.</violation>
</file>

<file name="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/api/CoverageApi.java">

<violation number="1" location="samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/api/CoverageApi.java:311">
P2: Standard Base64 values can contain `/`, but this route puts `bytes` in one path segment; a literal slash changes the route, and the default Spring Boot/Tomcat setup rejects an encoded `%2F`. Use a URL-safe representation for path values or keep arbitrary binary parameters out of the path.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 5 unresolved issues already reported by Cubic.

Re-trigger cubic

Comment thread samples/server/petstore/springboot-byte-format-edge-cases-reactive/README.md Outdated
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This build omits spring-boot-maven-plugin, so mvn package produces a plain JAR instead of a launchable Spring Boot archive. Add the plugin as in the existing Spring Boot sample.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-byte-format-edge-cases/pom.xml, line 27:

<comment>This build omits `spring-boot-maven-plugin`, so `mvn package` produces a plain JAR instead of a launchable Spring Boot archive. Add the plugin as in the existing Spring Boot sample.</comment>

<file context>
@@ -0,0 +1,84 @@
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. pom-sb3.mustache gates the plugin behind
{{^interfaceOnly}}, and this sample is generated with interfaceOnly: true (it
is a library-style interface/DTO sample, not meant to be packaged and run as a
launchable Spring Boot application on its own). The same pattern already exists in
several other pre-existing samples, including
spring-boot-defaultInterface-unhandledExcp, spring-boot-nullable-set, and
springboot-paged-model. No change made.

}


String PATH_PATH_PARAMS = "/coverage/path/{plain}/{bytes}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Standard Base64 values can contain /, but this route puts bytes in one path segment; a literal slash changes the route, and the default Spring Boot/Tomcat setup rejects an encoded %2F. Use a URL-safe representation for path values or keep arbitrary binary parameters out of the path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-byte-format-edge-cases/src/main/java/org/openapitools/api/CoverageApi.java, line 311:

<comment>Standard Base64 values can contain `/`, but this route puts `bytes` in one path segment; a literal slash changes the route, and the default Spring Boot/Tomcat setup rejects an encoded `%2F`. Use a URL-safe representation for path values or keep arbitrary binary parameters out of the path.</comment>

<file context>
@@ -0,0 +1,366 @@
+    }
+
+
+    String PATH_PATH_PARAMS = "/coverage/path/{plain}/{bytes}";
+    /**
+     * GET /coverage/path/{plain}/{bytes}
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid observation, but it describes an inherent property
of standard Base64 combined with using a value as a single path segment, not a
codegen defect. Standard Base64's alphabet includes /, which is a reserved
path-segment delimiter. Fixing this in generated code would require either
deviating from standard Base64 encoding (breaking OpenAPI spec compliance for
format: byte) or refusing to generate valid, if inadvisable, API designs that
put arbitrary binary data in a path segment. Since this specific edge case is
exercised by a test spec that this PR added, I added a documentation caveat
directly above the affected path parameter in byte-format-edge-cases.yaml,
describing the limitation and recommending against putting arbitrary Base64
values in path segments (or enabling encoded-slash handling on the server if a
caller depends on it). No behavioral or generator code change was made.

by adding ```@Controller``` classes that implement the interface. Eg:
```java
@Controller
public class PetController implements PetApi {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: These examples cannot compile against this sample because it exposes CoverageApi, not PetApi. Update the examples to use the sample's actual API or remove the unrelated Petstore boilerplate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/server/petstore/springboot-byte-format-edge-cases/README.md, line 16:

<comment>These examples cannot compile against this sample because it exposes `CoverageApi`, not `PetApi`. Update the examples to use the sample's actual API or remove the unrelated Petstore boilerplate.</comment>

<file context>
@@ -0,0 +1,27 @@
+by adding ```@Controller``` classes that implement the interface. Eg:
+```java
+@Controller
+public class PetController implements PetApi {
+// implement all PetApi methods
+}
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the standard interfaceOnly boilerplate produced by the
shared README.mustache template, and it is not specific to this sample. The
exact same generic PetApi usage example appears verbatim in the README of the
same pre-existing interfaceOnly samples referenced above (and others). It is a
long-standing, generator-wide documentation gap for the interfaceOnly mode, not
something introduced by this PR. No change made here; a fix would need to update
the shared template for all interfaceOnly samples at once.

Picazsoo and others added 4 commits September 27, 2026 14:13
- Close unclosed InputStream in blocking CoverageApiTestControllerImpl
- Rename misleading Kotlin parity test to reflect blocking-mode coverage
- Revert an incorrectly-renamed pre-existing Java test
  (...AsRequestParam back to ...AsRequestPart)
- Centralize the "/* base64 encoded binary */" comment into
  optionalDataType.mustache so it appears consistently across api,
  apiController, apiDelegate, and spring-http-interface templates
  (previously only duplicated in the per-location param partials)
- Add a "(base64 encoded)" note to the shared @PARAM Javadoc line for
  the same set of templates
- Extend convertByteArrayParamsToStringType to also convert
  array-of-byte params (List<byte[]> -> List<String>), since Spring
  cannot bind base64 array elements correctly either
- Make the multipart/form-data media-type check case-insensitive
  (RFC 7231)
- Fix reactive CoverageApiTestControllerImpl.multipartSimple/
  multipartSimpleValidated to reuse verifyBase64Content so a
  content-mismatch error is no longer mislabeled as "Invalid base64"
- Remove dead null/empty checks after MultipartMixedStatus.fromValue()
  (it only throws, never returns null)
- Document the base64-in-path-segment caveat (values may contain '/')
  directly on the CoverageApi path-param operation description
- Make the spring-boot library README mention Spring-WebFlux instead
  of Spring-MVC when reactive is enabled in interfaceOnly mode

Regenerated all affected samples (73 JavaSpring-template configs) to
verify only the intended comment/Javadoc/README changes were produced.
Verified with the full SpringCodegenTest (373) and
KotlinSpringServerCodegenTest (275) suites, plus mvn compile of both
byte-format-edge-cases samples and the spring-http-interface sample.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- RFC3339DateFormat.clone() in both byte-format-edge-cases samples now
  delegates to super.clone() instead of returning 'this', fixing a
  Cloneable/DateFormat contract violation flagged by review.
- Reactive CoverageApiTestControllerImpl.binaryBody now closes the
  request-body InputStream via try-with-resources.
- Reactive CoverageApiTestControllerImpl no longer leaks pooled
  DataBuffer instances: added a joinAndReleaseBuffers helper that
  releases each buffer after reading, used by multipartFileArray,
  multipartMixed, and multipartSimpleValidated.
- Fixed the HTML-escaped title in SpringDocConfiguration.java for the
  blocking sample (Multipart &amp; Byte Edge Case Coverage -> Multipart
  & Byte Edge Case Coverage). This file is hand-maintained (not
  reproduced by the sample's own interfaceOnly generator config), so it
  needed a direct, one-off fix rather than a spec/template change.

Remaining review findings (ApiUtil.java UTF-8 encoding, csharp-complex-files
Equals/GetHashCode and markerArray serialization, missing spring-boot-maven-plugin,
README PetApi boilerplate, base64-in-path-segment) were investigated and found to
be pre-existing, generator-wide behavior unrelated to this PR's scope, or
intentional given the sample's configuration; rationale documented separately.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added implementation of the generated interface to serve as a counterpart to the tests that I also implemented.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added tests to verify that the generated code actually works. As far as I can tell, these tests are in fact executed as a part of the build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added implementation of the generated interface to serve as a counterpart to the tests that I also implemented.

consumes = { "multipart/form-data" }
)
default Mono<ResponseEntity<Void>> multipartMixed(
@Parameter(name = "status", description = "", required = true) @Valid @RequestPart(value = "status", required = true) String /* MultipartMixedStatus */ status,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in webflux it is not possible to decode to the enum and the parameter has to be of type String.

Hence I am adding a conditional inline comment /* MultipartMixedStatus */ via mustache lambda to hint what it actually represents.

by adding ```@Controller``` classes that implement the interface. Eg:
```java
@Controller
public class PetController implements PetApi {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the standard interfaceOnly boilerplate produced by the
shared README.mustache template, and it is not specific to this sample. The
exact same generic PetApi usage example appears verbatim in the README of the
same pre-existing interfaceOnly samples referenced above (and others). It is a
long-standing, generator-wide documentation gap for the interfaceOnly mode, not
something introduced by this PR. No change made here; a fix would need to update
the shared template for all interfaceOnly samples at once.

@Picazsoo

Copy link
Copy Markdown
Contributor Author

Hey @cubic-dev-ai. Please re-review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Hey @cubic-dev-ai. Please re-review

@Picazsoo I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 111 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java">

<violation number="1" location="samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java:224">
P3: This conversion normalizes every text field in `testEndpointParameters` to `@RequestParam`, but the file field `binary` directly below remains an unconditional `@RequestPart ... Part`, in a method whose content type is pinned to `application/x-www-form-urlencoded`. A `Part`/`MultipartFile` can only be encoded as multipart, so a generated client cannot transmit `binary` for the declared content type, and the file parameter is effectively non-bindable/never received for urlencoded requests. The root cause is in the generator template `modules/openapi-generator/src/main/resources/JavaSpring/formParams.mustache`: the `{{#isFile}}` branch emits `@RequestPart ... Part/MultipartFile` unconditionally, while `markMultipartFormDataParameters` only switches reactive text fields based on media type. Gate the file-part annotation on the operation consuming `multipart/form-data`; otherwise emit `@RequestParam {{{dataType}}}` for the file field as well.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

@RequestParam(value = "number", required = true) BigDecimal number,
@RequestParam(value = "double", required = true) Double _double,
@RequestParam(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
@RequestParam(value = "byte", required = true) String _byte /* base64 encoded binary */,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This conversion normalizes every text field in testEndpointParameters to @RequestParam, but the file field binary directly below remains an unconditional @RequestPart ... Part, in a method whose content type is pinned to application/x-www-form-urlencoded. A Part/MultipartFile can only be encoded as multipart, so a generated client cannot transmit binary for the declared content type, and the file parameter is effectively non-bindable/never received for urlencoded requests. The root cause is in the generator template modules/openapi-generator/src/main/resources/JavaSpring/formParams.mustache: the {{#isFile}} branch emits @RequestPart ... Part/MultipartFile unconditionally, while markMultipartFormDataParameters only switches reactive text fields based on media type. Gate the file-part annotation on the operation consuming multipart/form-data; otherwise emit @RequestParam {{{dataType}}} for the file field as well.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java, line 224:

<comment>This conversion normalizes every text field in `testEndpointParameters` to `@RequestParam`, but the file field `binary` directly below remains an unconditional `@RequestPart ... Part`, in a method whose content type is pinned to `application/x-www-form-urlencoded`. A `Part`/`MultipartFile` can only be encoded as multipart, so a generated client cannot transmit `binary` for the declared content type, and the file parameter is effectively non-bindable/never received for urlencoded requests. The root cause is in the generator template `modules/openapi-generator/src/main/resources/JavaSpring/formParams.mustache`: the `{{#isFile}}` branch emits `@RequestPart ... Part/MultipartFile` unconditionally, while `markMultipartFormDataParameters` only switches reactive text fields based on media type. Gate the file-part annotation on the operation consuming `multipart/form-data`; otherwise emit `@RequestParam {{{dataType}}}` for the file field as well.</comment>

<file context>
@@ -218,20 +218,20 @@ Mono<ResponseEntity<Client>> testClientModel(
+         @RequestParam(value = "number", required = true) BigDecimal number,
+         @RequestParam(value = "double", required = true) Double _double,
+         @RequestParam(value = "pattern_without_delimiter", required = true) String patternWithoutDelimiter,
+         @RequestParam(value = "byte", required = true) String _byte /* base64 encoded binary */,
+         @RequestParam(value = "integer", required = false) Integer integer,
+         @RequestParam(value = "int32", required = false) Integer int32,
</file context>

Picazsoo and others added 2 commits September 27, 2026 22:34
- SpringCodegen.java: only log byte[]->String parameter conversions when
  the operation actually had any (avoids INFO-level log spam for every
  operation), and lower the log level to debug.
- SpringCodegenTest.java: add an assertion that the JSON body operation's
  request parameter stays typed as FormParamsRequest (the model the
  InlineModelResolver deduplicates the JSON body's inline schema into),
  covering the previously-unchecked byte[] JSON body case.
- csharp/api.mustache and csharp/ApiClient.mustache: make the
  multipart/form-data content-type checks case-insensitive, so the
  mixed-case 'Multipart/Form-Data' test operation (added earlier in this
  PR to validate cross-generator case-insensitive media-type handling)
  is actually sent as multipart/form-data by the C# httpclient/restsharp
  client. Regenerated csharp-complex-files to pick up the fix.
- Reactive CoverageApiTestControllerImpl.multipartSimple: read and
  release the file part's DataBuffers via the existing
  joinAndReleaseBuffers helper instead of dropping them unread, fixing a
  pooled-buffer leak.

Also investigated and found pre-existing/out-of-scope (rationale updated in the
review rebuttal doc): MultipartApi.cs convenience methods dropping
operationIndex (generator-wide, unrelated to byte-format/media-type handling),
and spring-http-interface-reactive FakeApi.java's binary parameter keeping
@RequestPart in a urlencoded operation (identical mismatch already present on
master in the blocking spring-http-interface sample).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 92 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java:1280">
P2: This assertion does not catch conversion of a JSON-body `byte[]` parameter because the fixture's body parameter is an object model. Add a scalar `format: byte` JSON body case and assert its generated parameter remains `byte[]`.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

JavaFileAssert.assertThat(files.get("JsonBodyApi.java"))
.assertMethod("jsonBody")
.assertParameter("formParamsRequest")
.hasType("FormParamsRequest");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This assertion does not catch conversion of a JSON-body byte[] parameter because the fixture's body parameter is an object model. Add a scalar format: byte JSON body case and assert its generated parameter remains byte[].

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java, line 1280:

<comment>This assertion does not catch conversion of a JSON-body `byte[]` parameter because the fixture's body parameter is an object model. Add a scalar `format: byte` JSON body case and assert its generated parameter remains `byte[]`.</comment>

<file context>
@@ -1268,6 +1268,17 @@ public void shouldHandleFormatByteCorrectlyForAllApiParametersAndProperties() th
+        JavaFileAssert.assertThat(files.get("JsonBodyApi.java"))
+                .assertMethod("jsonBody")
+                .assertParameter("formParamsRequest")
+                .hasType("FormParamsRequest");
+
         // Binary request body: bound as Resource for streaming
</file context>

Move the markerArray property and /multipart-mixed-case-media-type
operation (added by this branch for cross-generator case-insensitive
media-type coverage) out of the shared
form-multipart-binary-array.yaml fixture and into a new, dedicated
form-multipart-binary-array-extended.yaml spec.

- form-multipart-binary-array.yaml is restored to its pristine master
  content, so every consumer other than the tests that need the new
  coverage (Python FastAPI, TS Inversify/Axios, Java Jersey, JAX-RS
  spec, JavaScript, and the C# restsharp complexfiles sample) is
  unaffected.
- SpringCodegenTest (testMultipartBoot, testReactiveMultipartBoot) and
  JavaClientCodegenTest (testRestClientFormMultipart,
  testWebClientWithUseAbstractionForFiles,
  testRestClientWithUseAbstractionForFiles,
  testMicroprofileFormMultipartArray) now point at the new extended
  spec, since their assertions depend on markerArray.
- Reverted csharp/api.mustache and csharp/ApiClient.mustache to
  master; the case-insensitive content-type check is no longer needed
  since the C# generator never sees a mixed-case media type again.
- Reverted samples/client/others/csharp-complex-files and all other
  regenerated C# restsharp samples back to their pristine master
  state (zero diff against master).
- Updated PR-22930-review-rebuttal.md items covering the C# findings
  to note they are no longer part of this PR's diff.

Verified: SpringCodegenTest (373/373), JavaClientCodegenTest
(293/293), KotlinSpringServerCodegenTest, and all other incidental
spec consumers pass. git diff master against every reverted C# file
and sample returns zero lines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Picazsoo
Picazsoo marked this pull request as draft September 27, 2026 22:02
Picazsoo added a commit to Picazsoo/openapi-generator that referenced this pull request Sep 27, 2026
…e-format fix

Generator/template changes:
- Revert reactive multipart annotation logic in formParams.mustache back to
  unconditional @RequestPart for the array-non-model and scalar branches,
  matching master exactly. This fixes a compile-breaking type mismatch where
  the delegate/bridge methods declared a different type than the annotated
  method for non-byte scalar form fields in reactive multipart operations.
  Blocking (non-reactive) array params keep the newer, tested @RequestParam
  behavior.
- Remove the now-unused lambdaTypeComment Mustache lambda from
  SpringCodegen.java (its only call site was removed with the above fix).
- Add @Schema(type = "string", format = "byte") to paramDoc.mustache for
  non-array isByteArray parameters, so springdoc-generated API docs keep
  documenting the Base64 format contract after byte[] -> String conversion.
- Gate the Feign-client example in the spring-boot README template behind
  {{^reactive}}, since Spring Cloud OpenFeign does not support reactive
  Mono/Flux return types.

Sample changes (regenerated, plus two manually-maintained coverage
controllers):
- Reactive Fake/Pet API samples: annotation diffs reverted to match master,
  only byte-format-related diffs remain.
- 16 FakeApi.java samples across Spring variants: gained the new
  @Schema(format = "byte") on byte parameters.
- springboot-byte-format-edge-cases(-reactive): reverted the RFC3339DateFormat
  clone() hand-edit back to match the (separately pre-existing, unfixed)
  shared template; removed Feign example from the reactive README.
- CoverageApiTestControllerImpl.java (blocking + reactive): replaced unbounded
  buffering with a bounded inputStream.readNBytes() read in binaryBody
  (removing the duplicate readAllBytes helper); scheduled the reactive
  binaryBody's blocking read on Schedulers.boundedElastic(); released
  DataBuffers incrementally in the reactive multipartSimple instead of
  collecting them into a discarded byte[].

Investigated and confirmed as invalid, out of scope, or already correct
(documented in PR-review-rebuttal-round2.md, not committed here):
- README PetApi/CoverageApi name mismatch: same generic boilerplate as 10+
  other samples, pre-existing template limitation.
- Missing spring-boot-maven-plugin in interfaceOnly pom.xml: intentionally
  gated behind {{^interfaceOnly}}, consistent with all other interfaceOnly
  samples.
- multipartFileArray "unused" byte array: actually used for content
  comparison against expectedContents[i].
- java-camel stale version header: root-caused to OpenAPI Generator's
  built-in "test files are never overwritten by regeneration" behavior;
  confirmed pre-existing on master (7.11.0-SNAPSHOT there) and reproducible
  as a TemplateManager "Skipped" log line, unrelated to this PR and not
  fixable by regeneration.

Verified via full SpringCodegenTest/JavaClientCodegenTest/
KotlinSpringServerCodegenTest suites (953 tests, 0 failures), a full
regeneration diff of all Spring/kotlin-spring bin/configs, mvn compile/test
on both byte-format-edge-cases samples, and live curl checks against running
blocking and reactive servers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant