square/okhttp · error · IOException

Unexpected code

Error message

Unexpected code 

What it means

Thrown in the custom-cipher-suites recipe after a successful TLS handshake: `if (!response.isSuccessful()) throw new IOException("Unexpected code " + response)`. The custom ConnectionSpec restricts TLS to a handful of ECDHE cipher suites; if the server supports one of them the handshake succeeds and this line fires only on a non-2xx HTTP status. If none of the configured suites match, you get an SSLHandshakeException earlier, not this line.

Solutions

  1. Distinguish TLS handshake errors from HTTP status errors; this line means TLS worked.
  2. Inspect response.code() for the real HTTP status.
  3. If you also see handshake errors elsewhere, widen the ConnectionSpec cipher list.
  4. Branch on the code instead of throwing for clearer diagnostics.

Example fix

// before
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

// after
if (!response.isSuccessful()) {
  throw new IOException("HTTP " + response.code()
      + " (cipher=" + response.handshake().cipherSuite() + ")");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm at least one configured cipher is server-supported before relying on this client.
// (Best done by a smoke-test request; static checks of cipher names are fragile.)
try (Response r = client.newCall(smokeTestRequest).execute()) {
  if (!r.isSuccessful()) { /* HTTP status; TLS worked */ }
}

Type guard

static boolean handshakeUsedCustomSuite(Response r, List<CipherSuite> allowed) {
  return r.handshake() != null && allowed.contains(r.handshake().cipherSuite());
}

Try / catch

try {
  // call
} catch (SSLHandshakeException e) {
  // no common cipher suite with the server -> widen ConnectionSpec
} catch (IOException e) {
  // includes 'Unexpected code' (HTTP status) once TLS succeeded
}

Prevention

When it happens

Trigger: TLS handshake succeeded with one of the four configured suites against https://publicobject.com/helloworld.txt, but the server returned 404/403/5xx. (A cipher-suite mismatch would surface as SSLHandshakeException during connect, before any HTTP status exists.)

Common situations: Sample host removed the file; confusing a handshake failure (no common cipher) with this HTTP-status throw; restricting cipher suites so tightly that some servers cannot connect (but that is a different error).

Related errors


AI-assisted analysis of square/okhttp@91a8b34c6f (2026-08-10). Data as JSON: /api/errors/ee0bae5c7c6c62a5. Report an issue: GitHub.

Appendix: source

Thrown at samples/guide/src/main/java/okhttp3/recipes/CustomCipherSuites.java:161

    @Override public Socket createSocket(
        InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
      return configureSocket((SSLSocket) delegate.createSocket(
          address, port, localAddress, localPort));
    }

    protected SSLSocket configureSocket(SSLSocket socket) throws IOException {
      return socket;
    }
  }

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      System.out.println(response.handshake().cipherSuite());
      System.out.println(response.body().string());
    }
  }

  public static void main(String... args) throws Exception {
    new CustomCipherSuites().run();
  }
}

View on GitHub (pinned to 91a8b34c6f)