Conversation
…HttpClient ApiClient.getHttpClient() returns builder.build(), so every native API instance is handed a freshly built HttpClient that no other instance shares. That client was never released, leaking its connection pool and threads for the lifetime of the process. Generated API classes now implement AutoCloseable and close that client in close(), so they can be used in try-with-resources. HttpClient only became AutoCloseable in Java 21 while the native library targets Java 11, so the call is guarded by an instanceof check; on older runtimes there is no lifecycle API to call and close() is a no-op. The other Java client libraries are intentionally left alone. They hold a shared ApiClient (usually the process-wide Configuration singleton) or wrap a client that is not closeable at all, so an API instance owns nothing it would be safe to close.
There was a problem hiding this comment.
4 issues found across 36 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="modules/openapi-generator/src/main/resources/Java/libraries/native/api.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/native/api.mustache:968">
P1: When a specification has a parameterless operationId `close`, this template emits both the endpoint method and this lifecycle method with the same Java signature, so generation produces uncompilable code. Reserve or rename colliding operation IDs before rendering the API methods.</violation>
<violation number="2" location="modules/openapi-generator/src/main/resources/Java/libraries/native/api.mustache:968">
P2: Generated APIs force try-with-resources callers to handle checked `Exception`, although Java 21 `HttpClient.close()` has no checked exception. Remove `throws Exception` and translate the reflective/interface close failure to an unchecked exception.</violation>
</file>
<file name="samples/client/petstore/java/native/src/main/java/org/openapitools/client/api/UserApi.java">
<violation number="1" location="samples/client/petstore/java/native/src/main/java/org/openapitools/client/api/UserApi.java:1087">
P2: On Java 21+ this close() permanently shuts down the HttpClient, but the "owned exclusively" assumption is only true for the base ApiClient implementation. `ApiClient.getHttpClient()` is public and overridable (`protected HttpClient.Builder builder` is also meant for extension): a subclass that caches one shared HttpClient to avoid the per-instance leak this PR describes makes every API instance constructed from it share that client, and closing any one instance will close the client all other instances (and the subclass's own uses) depend on, failing their next request with IllegalStateException. The Javadoc asserts exclusivity without any way for close() to verify it. Document this contract on `ApiClient.getHttpClient()`/the class so subclasses and shared-client configurations are not silently broken, or capture ownership explicitly at construction.</violation>
</file>
<file name="samples/client/echo_api/java/native/src/main/java/org/openapitools/client/api/AuthApi.java">
<violation number="1" location="samples/client/echo_api/java/native/src/main/java/org/openapitools/client/api/AuthApi.java:380">
P3: The new close()-based resource lifecycle has no test coverage in this sample: AuthApiTest.java never calls close() or exercises the AutoCloseable path. Since this sample compiles with maven.compiler.source/target = 11, the `instanceof AutoCloseable` guard is always false on the baseline JVM, so the added behavior is silently a no-op in every test/CI run and would only be exercised on a Java 21+ runtime. Add a test (e.g., in AuthApiTest) that constructs an API instance, calls close(), and verifies it doesn't throw, at minimum catching regressions in the close() code path on any runtime.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| * on earlier runtimes there is no way to release the client's threads early. | ||
| */ | ||
| @Override | ||
| public void close() throws Exception { |
There was a problem hiding this comment.
P1: When a specification has a parameterless operationId close, this template emits both the endpoint method and this lifecycle method with the same Java signature, so generation produces uncompilable code. Reserve or rename colliding operation IDs before rendering the API methods.
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/native/api.mustache, line 968:
<comment>When a specification has a parameterless operationId `close`, this template emits both the endpoint method and this lifecycle method with the same Java signature, so generation produces uncompilable code. Reserve or rename colliding operation IDs before rendering the API methods.</comment>
<file context>
@@ -955,5 +955,20 @@ public class {{classname}} {
+ * on earlier runtimes there is no way to release the client's threads early.
+ */
+ @Override
+ public void close() throws Exception {
+ if (memberVarHttpClient instanceof AutoCloseable) {
+ ((AutoCloseable) memberVarHttpClient).close();
</file context>
| */ | ||
| @Override | ||
| public void close() throws Exception { | ||
| if (memberVarHttpClient instanceof AutoCloseable) { |
There was a problem hiding this comment.
P2: On Java 21+ this close() permanently shuts down the HttpClient, but the "owned exclusively" assumption is only true for the base ApiClient implementation. ApiClient.getHttpClient() is public and overridable (protected HttpClient.Builder builder is also meant for extension): a subclass that caches one shared HttpClient to avoid the per-instance leak this PR describes makes every API instance constructed from it share that client, and closing any one instance will close the client all other instances (and the subclass's own uses) depend on, failing their next request with IllegalStateException. The Javadoc asserts exclusivity without any way for close() to verify it. Document this contract on ApiClient.getHttpClient()/the class so subclasses and shared-client configurations are not silently broken, or capture ownership explicitly at construction.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/java/native/src/main/java/org/openapitools/client/api/UserApi.java, line 1087:
<comment>On Java 21+ this close() permanently shuts down the HttpClient, but the "owned exclusively" assumption is only true for the base ApiClient implementation. `ApiClient.getHttpClient()` is public and overridable (`protected HttpClient.Builder builder` is also meant for extension): a subclass that caches one shared HttpClient to avoid the per-instance leak this PR describes makes every API instance constructed from it share that client, and closing any one instance will close the client all other instances (and the subclass's own uses) depend on, failing their next request with IllegalStateException. The Javadoc asserts exclusivity without any way for close() to verify it. Document this contract on `ApiClient.getHttpClient()`/the class so subclasses and shared-client configurations are not silently broken, or capture ownership explicitly at construction.</comment>
<file context>
@@ -1073,4 +1073,19 @@ private HttpRequest.Builder updateUserRequestBuilder(@javax.annotation.Nonnull S
+ */
+ @Override
+ public void close() throws Exception {
+ if (memberVarHttpClient instanceof AutoCloseable) {
+ ((AutoCloseable) memberVarHttpClient).close();
+ }
</file context>
| public void close() throws Exception { | ||
| if (memberVarHttpClient instanceof AutoCloseable) { | ||
| ((AutoCloseable) memberVarHttpClient).close(); | ||
| } | ||
| } |
There was a problem hiding this comment.
P2: Generated APIs force try-with-resources callers to handle checked Exception, although Java 21 HttpClient.close() has no checked exception. Remove throws Exception and translate the reflective/interface close failure to an unchecked exception.
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/native/api.mustache, line 968:
<comment>Generated APIs force try-with-resources callers to handle checked `Exception`, although Java 21 `HttpClient.close()` has no checked exception. Remove `throws Exception` and translate the reflective/interface close failure to an unchecked exception.</comment>
<file context>
@@ -955,5 +955,20 @@ public class {{classname}} {
+ * on earlier runtimes there is no way to release the client's threads early.
+ */
+ @Override
+ public void close() throws Exception {
+ if (memberVarHttpClient instanceof AutoCloseable) {
+ ((AutoCloseable) memberVarHttpClient).close();
</file context>
| public void close() throws Exception { | |
| if (memberVarHttpClient instanceof AutoCloseable) { | |
| ((AutoCloseable) memberVarHttpClient).close(); | |
| } | |
| } | |
| public void close() { | |
| if (memberVarHttpClient instanceof AutoCloseable) { | |
| try { | |
| ((AutoCloseable) memberVarHttpClient).close(); | |
| } catch (RuntimeException e) { | |
| throw e; | |
| } catch (Exception e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| } |
| * on earlier runtimes there is no way to release the client's threads early. | ||
| */ | ||
| @Override | ||
| public void close() throws Exception { |
There was a problem hiding this comment.
P3: The new close()-based resource lifecycle has no test coverage in this sample: AuthApiTest.java never calls close() or exercises the AutoCloseable path. Since this sample compiles with maven.compiler.source/target = 11, the instanceof AutoCloseable guard is always false on the baseline JVM, so the added behavior is silently a no-op in every test/CI run and would only be exercised on a Java 21+ runtime. Add a test (e.g., in AuthApiTest) that constructs an API instance, calls close(), and verifies it doesn't throw, at minimum catching regressions in the close() code path on any runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/echo_api/java/native/src/main/java/org/openapitools/client/api/AuthApi.java, line 380:
<comment>The new close()-based resource lifecycle has no test coverage in this sample: AuthApiTest.java never calls close() or exercises the AutoCloseable path. Since this sample compiles with maven.compiler.source/target = 11, the `instanceof AutoCloseable` guard is always false on the baseline JVM, so the added behavior is silently a no-op in every test/CI run and would only be exercised on a Java 21+ runtime. Add a test (e.g., in AuthApiTest) that constructs an API instance, calls close(), and verifies it doesn't throw, at minimum catching regressions in the close() code path on any runtime.</comment>
<file context>
@@ -367,4 +367,19 @@ private HttpRequest.Builder testAuthHttpBearerRequestBuilder(Map<String, String>
+ * on earlier runtimes there is no way to release the client's threads early.
+ */
+ @Override
+ public void close() throws Exception {
+ if (memberVarHttpClient instanceof AutoCloseable) {
+ ((AutoCloseable) memberVarHttpClient).close();
</file context>
ApiClient.getHttpClient() returns builder.build(), so every native API instance is handed a freshly built HttpClient that no other instance shares. That client was never released, leaking its connection pool and threads for the lifetime of the process.
Generated API classes now implement AutoCloseable and close that client in close(), so they can be used in try-with-resources. HttpClient only became AutoCloseable in Java 21 while the native library targets Java 11, so the call is guarded by an instanceof check; on older runtimes there is no lifecycle API to call and close() is a no-op.
The other Java client libraries are intentionally left alone. They hold a shared ApiClient (usually the process-wide Configuration singleton) or wrap a client that is not closeable at all, so an API instance owns nothing it would be safe to close.
Note the current
masterdoes not build cleanly for me on windows either natively or in WSL, so several tests will only have been run in CI (tried with a variety of different JVMs, 25 fails with:11 fails with:
followed by
fixes: #24942
@bbdouglas @sreeshas @jfiala @lukoyanov @cbornet @jeff9finger @karismann @Zomzog @lwlee2608 @martin-mfg @KannaKim
PR checklist
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.