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

  1. Set both together, deriving the trust manager from the same SSLContext/KeyStore as the socket factory.
  2. Remove both to use the system default TLS configuration.
  3. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/04931c7bdedf58b3. Report an issue: GitHub.