{"record":{"id":"da1683f054ee79d8","repo":"spring-projects/spring-ai","slug":"both-or-none-of-maxidleconnections-and-keepaliv","errorCode":null,"errorMessage":"Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set","messagePattern":"Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/http/okhttp/SpringAiAnthropicHttpClient.java","lineNumber":637,"sourceCode":"\t\t\t\t\t\t\ttoHttpResponse(response));\n\t\t\t\t\treturn authed.map(req -> toRequestComputeUrl(req, null)).orElse(null);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tExecutorService userDispatcherExecutor = this.dispatcherExecutorService;\n\t\t\tboolean ownsDispatcherExecutor = userDispatcherExecutor == null;\n\t\t\tExecutorService dispatcherBase = (userDispatcherExecutor != null) ? userDispatcherExecutor\n\t\t\t\t\t: defaultDispatcherExecutor();\n\t\t\tExecutorService dispatcherExecutor = ContextExecutorService.wrap(dispatcherBase,\n\t\t\t\t\tContextSnapshotFactory.builder().build());\n\t\t\tokBuilder.dispatcher(new Dispatcher(dispatcherExecutor));\n\n\t\t\tif (this.maxIdleConnections != null && this.keepAliveDuration != null) {\n\t\t\t\tokBuilder.connectionPool(new ConnectionPool(this.maxIdleConnections, this.keepAliveDuration.toNanos(),\n\t\t\t\t\t\tTimeUnit.NANOSECONDS));\n\t\t\t}\n\t\t\telse if ((this.maxIdleConnections == null) != (this.keepAliveDuration == null)) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set\");\n\t\t\t}\n\n\t\t\tif (this.sslSocketFactory != null && this.trustManager != null) {\n\t\t\t\tokBuilder.sslSocketFactory(this.sslSocketFactory, this.trustManager);\n\t\t\t}\n\t\t\telse if ((this.sslSocketFactory == null) != (this.trustManager == null)) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\t\"Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set\");\n\t\t\t}\n\n\t\t\tif (this.hostnameVerifier != null) {\n\t\t\t\tokBuilder.hostnameVerifier(this.hostnameVerifier);\n\t\t\t}\n\n\t\t\tOkHttpClient okClient = okBuilder.build();\n\t\t\t// Same-host traffic: raise per-host limit to overall request limit. Matches\n\t\t\t// the SDK's tuning at the bottom of `OkHttpClient.Builder.build()`.","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/http/okhttp/SpringAiAnthropicHttpClient.java#L619-L655","documentation":"The SpringAiAnthropicHttpClient.Builder's build() validates that the OkHttp connection-pool tuning options are set as a pair. If exactly one of maxIdleConnections or keepAliveDuration was provided, it throws IllegalStateException, because a ConnectionPool requires both values and a half-specified pool is a configuration mistake.","triggerScenarios":"Building the client with only maxIdleConnections set, or only keepAliveDuration set, e.g. clientBuilder().maxIdleConnections(5).build().","commonSituations":"Copying a tuning snippet from docs and setting one field; conditional configuration code that sets one property but not the other; refactoring that dropped one of the two builder calls.","solutions":["Set both values together: .maxIdleConnections(n).keepAliveDuration(Duration.ofMinutes(5)).","If tuning is not needed, remove both settings and use OkHttp's default pool.","Review your configuration binding so that the two properties are always applied as a unit (e.g. a single ConnectionPool config object)."],"exampleFix":"// before\nSpringAiAnthropicHttpClient.newBuilder().maxIdleConnections(5).build(); // throws\n// after\nSpringAiAnthropicHttpClient.newBuilder()\n    .maxIdleConnections(5)\n    .keepAliveDuration(Duration.ofMinutes(5))\n    .build();","handlingStrategy":"validation","validationCode":"static void checkPoolConfig(Integer maxIdle, Duration keepAlive) {\n    if ((maxIdle == null) != (keepAlive == null)) {\n        throw new IllegalStateException(\"maxIdleConnections and keepAliveDuration must both be set or both null\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return SpringAiAnthropicHttpClient.newBuilder()...build();\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"maxIdleConnections\")) {\n        // fall back to defaults or apply both settings\n    }\n    throw e;\n}","preventionTips":["Encapsulate pool tuning in one config object so the pair is always set together.","Never conditionally set only one of the two pool options.","Add a startup-time config validation test for your client builder."],"tags":["configuration","builder","connection-pool"],"backgroundTag":"conflicting-config-options","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}