spring-projects/spring-ai · error · IllegalStateException
Both or none of `sslSocketFactory` and `trustManager` must b
Error message
Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set
What it means
Builder consistency check in SpringAiOpenAiHttpClient.build: sslSocketFactory and trustManager are coupled TLS settings and exactly one of them was provided; partial TLS customization would leave the client in an inconsistent state.
Source
Thrown at models/spring-ai-openai/src/main/java/org/springframework/ai/openai/http/okhttp/SpringAiOpenAiHttpClient.java:583
: defaultDispatcherExecutor();
ExecutorService dispatcherExecutor = ContextExecutorService.wrap(dispatcherBase,
ContextSnapshotFactory.builder().build());
okBuilder.dispatcher(new Dispatcher(dispatcherExecutor));
if (this.maxIdleConnections != null && this.keepAliveDuration != null) {
okBuilder.connectionPool(new ConnectionPool(this.maxIdleConnections, this.keepAliveDuration.toNanos(),
TimeUnit.NANOSECONDS));
}
else if ((this.maxIdleConnections == null) != (this.keepAliveDuration == null)) {
throw new IllegalStateException(
"Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set");
}
if (this.sslSocketFactory != null && this.trustManager != null) {
okBuilder.sslSocketFactory(this.sslSocketFactory, this.trustManager);
}
else if ((this.sslSocketFactory == null) != (this.trustManager == null)) {
throw new IllegalStateException(
"Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set");
}
if (this.hostnameVerifier != null) {
okBuilder.hostnameVerifier(this.hostnameVerifier);
}
OkHttpClient okClient = okBuilder.build();
// Same-host traffic: raise per-host limit to overall request limit. Matches
// the SDK's tuning at the bottom of `OkHttpClient.Builder.build()`.
okClient.dispatcher().setMaxRequestsPerHost(okClient.dispatcher().getMaxRequests());
if (this.meterRegistry != null) {
new OkHttpConnectionPoolMetrics(okClient.connectionPool(), this.meterTags).bindTo(this.meterRegistry);
}
return new SpringAiOpenAiHttpClient(okClient, ownsDispatcherExecutor);
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Set both together, deriving the trust manager from the same SSLContext/KeyStore as the socket factory.
- Remove both to use the system default TLS configuration.
- Build the client in an init/health check so the misconfiguration surfaces at startup.
Example fix
// before builder().sslSocketFactory(sslContext.getSocketFactory()).build(); // after TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); builder().sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) tmf.getTrustManagers()[0]).build();
Defensive patterns
Strategy: validation
Validate before calling
if ((sslSocketFactory == null) != (trustManager == null)) {
throw new IllegalStateException("Set both sslSocketFactory and trustManager, or neither");
} Try / catch
try {
client = SpringAiOpenAiHttpClient.builder()
.sslSocketFactory(factory, trustManager)
.build();
} catch (IllegalStateException e) {
throw new ClientConfigurationException("Invalid TLS configuration: " + e.getMessage(), e);
} Prevention
- Derive sslSocketFactory and trustManager from the same SSLContext
- Configure custom CAs via one helper that sets both
- Eagerly build the client during application startup
When it happens
Trigger: Calling builder().sslSocketFactory(factory).build() without trustManager (or the reverse), detected at build() in SpringAiOpenAiHttpClient.java:583.
Common situations: Custom CA / mutual-TLS setups where a developer sets the socket factory but forgets the trust manager, or properties files that populate one TLS setting but not its pair.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Both or none of `sslSocketFactory` and `trustManager` must b
- MessageEndpoint must be set
- DataSource must be set (either via dataSource() or jdbcTempl
- Both or none of `maxIdleConnections` and `keepAliveDuration`
- Both or none of `maxIdleConnections` and `keepAliveDuration`
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/04931c7bdedf58b3.
Report an issue: GitHub.