square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Recipe-level guard after a GET over a client configured with a custom ConnectionSpec that restricts cipher suites to four ECDHE AEAD suites and a DelegatingSSLSocketFactory that forces those suites on each socket. Reaching this line means the TLS handshake negotiated one of the configured suites; if the HTTP response is then non-2xx, java.io.IOException("Unexpected code " + response) is thrown. A cipher-suite negotiation FAILURE surfaces earlier as SSLHandshakeException, not here.
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 4fc0831380)
Solutions
- Inspect response.handshake().cipherSuite() — if it is one of the four configured, the handshake is fine and this is a plain HTTP error.
- If you are getting SSLHandshakeException instead, broaden the ConnectionSpec or fall back to ConnectionSpec.MODERN_TLS.
- Log response.code() to separate HTTP errors from cipher/TLS errors.
Example fix
// before
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
...
}
// after
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("HTTP " + response.code()
+ " (negotiated cipher " + response.handshake().cipherSuite() + ")");
}
...
} Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the server supports at least one of your configured suites before relying on it // (e.g. with sslscan or openssl s_client -connect host:443 -cipher 'ECDHE-ECDSA-AES128-GCM-SHA256') // In code, broaden the spec as a fallback if negotiation fails.
Try / catch
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new HttpException(response.code(), response.message());
} catch (SSLHandshakeException e) {
// No overlap between your 4 configured suites and the server
throw new TlsConfigException("Server supports none of the configured cipher suites", e);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Unexpected code")) {
// handshake succeeded; this is a plain HTTP error
}
throw e;
} Prevention
- Confirm cipher-suite overlap with the server (openssl s_client / sslscan) before restricting.
- Log response.handshake().cipherSuite() to verify what was actually negotiated.
- Keep ConnectionSpec.MODERN_TLS as a fallback when a custom spec fails.
- Most apps should NOT customise cipher suites — the recipe warns about this.
When it happens
Trigger: GET https://publicobject.com/helloworld.txt through the customised client. Reaches this line on a non-2xx after a successful handshake; surfaces when the server lacks overlap with the four configured suites (but that throws earlier), or when the origin returns 4xx/5xx.
Common situations: Over-restricting cipher suites so older server stacks cannot negotiate (would surface as handshake failure, not this); assuming this exception is about ciphers when it is actually an HTTP error; server-side 5xx during maintenance.
Related errors
AI-assisted analysis of square/okhttp@4fc0831380 (2026-08-04).
Data as JSON: /data/errors/ee0bae5c7c6c62a5.json.
Report an issue: GitHub.