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

  1. If you installed a custom Provider, filter for the X509TrustManager instance instead of asserting a single element.
  2. Avoid registering your provider as the default for TrustManagerFactory if you only need it for sockets.
  3. Check java.security property ssl.TrustManagerFactory.algorithm and set it back to PKIX if it was changed.
  4. 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

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)