Skip to content

fix(java/restclient): avoid IndexOutOfBoundsException for empty multi…#23314

Open
JohnBryte wants to merge 1 commit intoOpenAPITools:masterfrom
JohnBryte:fix/23153-multipartformdata-index-out-of-bounds-empty-list
Open

fix(java/restclient): avoid IndexOutOfBoundsException for empty multi…#23314
JohnBryte wants to merge 1 commit intoOpenAPITools:masterfrom
JohnBryte:fix/23153-multipartformdata-index-out-of-bounds-empty-list

Conversation

@JohnBryte
Copy link
Contributor

@JohnBryte JohnBryte commented Mar 22, 2026

…part list params (#23153)

PR checklist

  • [ x] Read the contribution guidelines.
  • [x ] 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.
  • [ x] 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.
  • [ x] 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)
  • [ x] 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.

Description

This PR fixes an issue in the java generator (restclient library) where multipart form parameters containing lists could cause an IndexOutOfBoundsException.

Root cause

The existing implementation:

  • Assumes list-based multipart parameters are non-empty and directly accesses v.get(0)
  • Uses the first element only as a runtime type probe (due to type erasure)
  • Detects enum lists via getEnumConstants() != null
  • Converts only the first element of the list to String, leaving the rest unchanged
  • Restricts handling to ArrayList, excluding other List implementations

This leads to:

  • A crash when an empty list is passed

Fix

  • Add a guard to ensure the list is not empty before accessing elements
  • Replace getEnumConstants() with isEnum() for clearer intent

Updated logic:

if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
    formParams.forEach((k, v) -> {
                    if (v instanceof java.util.ArrayList && !v.isEmpty()) {
                        Object first = v.get(0);
                        if (first != null && first.getClass().isEnum()) {
                            v.set(0, first.toString());
                        }
                    }
    });
}

This preserves the original intent (enum list handling) while making it safe and consistent.

Tests

Added a regression test in JavaClientCodegenTest to verify that:

Generated ApiClient.java contains the new guarded logic


Summary by cubic

Fixes an IndexOutOfBoundsException for empty multipart/form-data list params in java restclient by adding an empty-list guard and safer enum detection.

  • Bug Fixes
    • Guard against empty ArrayList before accessing index 0 in multipart form params.
    • Detect enums with isEnum() and convert the first enum item to a string.
    • Added a regression test and regenerated samples.

Written for commit e0d29ae. Summary will update on new commits.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Contributor

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

Choose a reason for hiding this comment

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

5 issues found across 13 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="samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient-useSingleRequestParameter-static/src/main/java/org/openapitools/client/ApiClient.java:805">
P2: Multipart enum-list conversion mutates arbitrary `List<?>` values in place, which can throw `UnsupportedOperationException` for immutable/fixed-size lists supplied in `formParams`.</violation>
</file>

<file name="samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient-nullable-arrays/src/main/java/org/openapitools/client/ApiClient.java:732">
P2: Multipart enum normalization now mutates any `List<?>`, which can throw `UnsupportedOperationException` for immutable/fixed-size list implementations.</violation>
</file>

<file name="samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient-swagger2/src/main/java/org/openapitools/client/ApiClient.java:805">
P2: Multipart enum-list normalization mutates generic `List<?>` in place and can throw `UnsupportedOperationException` on immutable/unmodifiable lists.</violation>
</file>

<file name="samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/others/java/restclient-enum-in-multipart/src/main/java/org/openapitools/client/ApiClient.java:733">
P2: Multipart enum normalization mutates arbitrary `List` values in place; immutable lists can trigger `UnsupportedOperationException` at runtime.</violation>
</file>

<file name="samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java:732">
P2: Multipart enum normalization mutates caller-provided lists in place, which can fail on non-mutable lists and causes side effects on input data.</violation>
</file>

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

Copy link
Contributor

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

Choose a reason for hiding this comment

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

5 issues found across 13 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="samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java:750">
P2: Multipart form parameter order can become unstable because `HashMap` is used as the intermediate normalization container before repopulating `formParams`.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache:865">
P1: `formParams.putAll(normalizedParams)` passes `Map<String, Object>` where `MultiValueMap<String, Object>` requires map values of `List<Object>`, causing a compile-time generic type mismatch.</violation>
</file>

<file name="samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java:797">
P2: Multipart form field order can become unstable because params are rebuilt through an unordered `HashMap` before re-inserting into `formParams`.</violation>
</file>

<file name="samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java:759">
P2: Multipart normalization now requires mutating `formParams` (`clear`/`putAll`), which can fail for immutable `MultiValueMap` inputs passed to public `invokeAPI`.</violation>
</file>

<file name="samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java">

<violation number="1" location="samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java:724">
P2: Multipart form parameter normalization now uses `HashMap`, which can reorder parts and change request part order nondeterministically.</violation>
</file>

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

normalizedParams.put(k, normalizedValue);
});
formParams.clear();
formParams.putAll(normalizedParams);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 22, 2026

Choose a reason for hiding this comment

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

P1: formParams.putAll(normalizedParams) passes Map<String, Object> where MultiValueMap<String, Object> requires map values of List<Object>, causing a compile-time generic type mismatch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/Java/libraries/restclient/ApiClient.mustache, line 865:

<comment>`formParams.putAll(normalizedParams)` passes `Map<String, Object>` where `MultiValueMap<String, Object>` requires map values of `List<Object>`, causing a compile-time generic type mismatch.</comment>

<file context>
@@ -845,20 +845,24 @@ public class ApiClient{{#jsr310}} extends JavaTimeFormatter{{/jsr310}} {
+                normalizedParams.put(k, normalizedValue);
             });
+            formParams.clear();
+            formParams.putAll(normalizedParams);
         }
 
</file context>
Fix with Cubic

if (o != null && o.getClass().getEnumConstants() != null) {
v.set(0, o.toString());
}
Map<String, Object> normalizedParams = new HashMap<>();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 22, 2026

Choose a reason for hiding this comment

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

P2: Multipart form parameter order can become unstable because HashMap is used as the intermediate normalization container before repopulating formParams.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/restclient-springBoot4-jackson2/src/main/java/org/openapitools/client/ApiClient.java, line 750:

<comment>Multipart form parameter order can become unstable because `HashMap` is used as the intermediate normalization container before repopulating `formParams`.</comment>

<file context>
@@ -747,20 +747,24 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth
         addCookiesToRequest(defaultCookies, requestBuilder);
 
         if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
+            Map<String, Object> normalizedParams = new HashMap<>();
             formParams.forEach((k, v) -> {
+                Object normalizedValue = v;
</file context>
Suggested change
Map<String, Object> normalizedParams = new HashMap<>();
+ Map<String, Object> normalizedParams = new java.util.LinkedHashMap<>();
Fix with Cubic

if (o != null && o.getClass().getEnumConstants() != null) {
v.set(0, o.toString());
}
Map<String, Object> normalizedParams = new HashMap<>();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 22, 2026

Choose a reason for hiding this comment

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

P2: Multipart form field order can become unstable because params are rebuilt through an unordered HashMap before re-inserting into formParams.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/restclient/src/main/java/org/openapitools/client/ApiClient.java, line 797:

<comment>Multipart form field order can become unstable because params are rebuilt through an unordered `HashMap` before re-inserting into `formParams`.</comment>

<file context>
@@ -794,20 +794,24 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth
         addCookiesToRequest(defaultCookies, requestBuilder);
 
         if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
+            Map<String, Object> normalizedParams = new HashMap<>();
             formParams.forEach((k, v) -> {
+                Object normalizedValue = v;
</file context>
Suggested change
Map<String, Object> normalizedParams = new HashMap<>();
Map<String, Object> normalizedParams = new java.util.LinkedHashMap<>();
Fix with Cubic

}
normalizedParams.put(k, normalizedValue);
});
formParams.clear();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 22, 2026

Choose a reason for hiding this comment

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

P2: Multipart normalization now requires mutating formParams (clear/putAll), which can fail for immutable MultiValueMap inputs passed to public invokeAPI.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/restclient-springBoot4-jackson3/src/main/java/org/openapitools/client/ApiClient.java, line 759:

<comment>Multipart normalization now requires mutating `formParams` (`clear`/`putAll`), which can fail for immutable `MultiValueMap` inputs passed to public `invokeAPI`.</comment>

<file context>
@@ -740,20 +740,24 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth
                 }
+                normalizedParams.put(k, normalizedValue);
             });
+            formParams.clear();
+            formParams.putAll(normalizedParams);
         }
</file context>
Fix with Cubic

if (o != null && o.getClass().getEnumConstants() != null) {
v.set(0, o.toString());
}
Map<String, Object> normalizedParams = new HashMap<>();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 22, 2026

Choose a reason for hiding this comment

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

P2: Multipart form parameter normalization now uses HashMap, which can reorder parts and change request part order nondeterministically.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/others/java/restclient-sealedInterface/src/main/java/org/openapitools/client/ApiClient.java, line 724:

<comment>Multipart form parameter normalization now uses `HashMap`, which can reorder parts and change request part order nondeterministically.</comment>

<file context>
@@ -721,20 +721,24 @@ protected RestClient.RequestBodySpec prepareRequest(String path, HttpMethod meth
         addCookiesToRequest(defaultCookies, requestBuilder);
 
         if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
+            Map<String, Object> normalizedParams = new HashMap<>();
             formParams.forEach((k, v) -> {
+                Object normalizedValue = v;
</file context>
Suggested change
Map<String, Object> normalizedParams = new HashMap<>();
Map<String, Object> normalizedParams = new java.util.LinkedHashMap<>();
Fix with Cubic

Map<String, Object> normalizedParams = new HashMap<>();
formParams.forEach((k, v) -> {
Object normalizedValue = v;
if (v instanceof List<?> && !((List<?>) v).isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. v instanceof List<?> list (cast, declare and assign in one step)
  2. Move empty list check into the if block. this will make it much easier to see how "normalizedValue" is potentially a redundant variable. It will also make it obvious that get(0) is safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately using pattern matching like v instanceof List<?> list is not possible because the generators would need to support older Java versions. But maybe I'm mistaken here as I'm still pretty new to the project

Copy link
Contributor

@Chrimle Chrimle left a comment

Choose a reason for hiding this comment

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

Minor refactors, but concerns about modifying the original formParams beyond the scope of enum-entries.

}
normalizedParams.put(k, normalizedValue);
});
formParams.clear();
Copy link
Contributor

Choose a reason for hiding this comment

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

This is questionable. Before, the behavior was to replace a list of enums with a single entry enum string.

now, this would modify non-enum entries (by removing them completely)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As mentioned below, I now reduced this PR to just tackle the original issue.
The fix is now much smaller in scope. I would open a new issue concerning the normalizing of all enum elements

@JohnBryte JohnBryte force-pushed the fix/23153-multipartformdata-index-out-of-bounds-empty-list branch from c0eaf2b to e0d29ae Compare March 23, 2026 08:15
@JohnBryte
Copy link
Contributor Author

Thanks for all your feedback @Chrimle
I reduced this PR to a minimal fix for the reported crash. The only behavioral change is guarding against empty multipart lists before accessing index 0.

I now did not address the broader question of whether only the first enum element should be normalized, since that appears to be a separate issue in the original implementation and would expand the scope of this PR.

Changing this behavior could also introduce backwards incompatibility, as existing code might implicitly rely on the current behavior (even if it is inconsistent). I think this should be discussed separately, so I’m happy to open a follow-up issue to evaluate a more complete fix.

@JohnBryte JohnBryte requested a review from Chrimle March 23, 2026 08:54
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.

2 participants