square/okhttp · error · IllegalStateException
Unexpected default trust managers:
Error message
Unexpected default trust managers:
What it means
Thrown during client construction in CustomCipherSuites.defaultTrustManager(): `throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers))`. It asks the JVM's TrustManagerFactory (default algorithm) for the platform trust managers and asserts that exactly one X509TrustManager is returned. On standard OpenJDK/Android this is always one; the guard exists because some custom security providers or non-default algorithms return a different array shape. This is a startup/configuration failure, not a request-time error.
Solutions
- If you installed a custom Provider, filter for the X509TrustManager instance instead of asserting a single element.
- Avoid registering your provider as the default for TrustManagerFactory if you only need it for sockets.
- Check java.security property ssl.TrustManagerFactory.algorithm and set it back to PKIX if it was changed.
- Loosen the assertion: iterate trustManagers and pick the first X509TrustManager rather than failing.
Example fix
// before
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
// after
for (TrustManager tm : trustManagers) {
if (tm instanceof X509TrustManager) return (X509TrustManager) tm;
}
throw new IllegalStateException("No X509TrustManager among: " + Arrays.toString(trustManagers)); Defensive patterns
Strategy: validation
Validate before calling
// Validate trust managers defensively instead of asserting length==1.
X509TrustManager tm = null;
for (TrustManager m : trustManagerFactory.getTrustManagers()) {
if (m instanceof X509TrustManager) { tm = (X509TrustManager) m; break; }
}
if (tm == null) throw new IllegalStateException("no X509TrustManager available"); Type guard
static Optional<X509TrustManager> firstX509(TrustManager[] tms) {
for (TrustManager m : tms) if (m instanceof X509TrustManager) return Optional.of((X509TrustManager) m);
return Optional.empty();
} Try / catch
try {
client = buildClient(); // calls defaultTrustManager()
} catch (IllegalStateException e) {
// 'Unexpected default trust managers' -> custom provider/algorithm; fall back to platform default
client = new OkHttpClient();
} Prevention
- Do not register a custom Provider as the default TrustManagerFactory unless required.
- Iterate trust managers and pick the first X509TrustManager rather than asserting a single one.
- Check the java.security property ssl.TrustManagerFactory.algorithm if you see this.
- Test client construction on the target JVM/Android runtime early.
When it happens
Trigger: A custom PKIX/security Provider is registered (e.g. BouncyCastle as a javax.net.ssl provider, Conscrypt, or a corporate PKI agent) that makes TrustManagerFactory.getTrustManagers() return zero, more than one, or a non-X509TrustManager array. Also possible if the default algorithm was changed via ssl.TrustManagerFactory.algorithm in java.security.
Common situations: Adding a security provider that overrides the default TrustManagerFactory; running inside a managed/corporate JRE with a custom trust configuration; running on an unusual JVM where the default algorithm returns multiple managers per keystore types.
Related errors
AI-assisted analysis of square/okhttp@91a8b34c6f (2026-08-10).
Data as JSON: /api/errors/cd2fa4b2ae508091.
Report an issue: GitHub.
Appendix: source
Thrown at samples/guide/src/main/java/okhttp3/recipes/CustomCipherSuites.java:93
* Returns the VM's default SSL socket factory, using {@code trustManager} for trusted root
* certificates.
*/
private SSLSocketFactory defaultSslSocketFactory(X509TrustManager trustManager)
throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
return sslContext.getSocketFactory();
}
/** Returns a trust manager that trusts the VM's default certificate authorities. */
private X509TrustManager defaultTrustManager() throws GeneralSecurityException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
private String[] javaNames(List<CipherSuite> cipherSuites) {
String[] result = new String[cipherSuites.size()];
for (int i = 0; i < result.length; i++) {
result[i] = cipherSuites.get(i).javaName();
}
return result;
}
/**
* An SSL socket factory that forwards all calls to a delegate. Override {@link #configureSocket}
* to customize a created socket before it is returned.
*/
static class DelegatingSSLSocketFactory extends SSLSocketFactory {View on GitHub (pinned to 91a8b34c6f)