square/okhttp · error · IOException
Unexpected code
Error message
Unexpected code
What it means
Recipe-level guard inside a client built with a custom trust store (HandshakeCertificates trusting only Comodo RSA, Entrust Root, and Let's Encrypt X3 roots). The recipe first dumps response headers, then throws java.io.IOException("Unexpected code " + response) for non-2xx. Important: if the server's chain does NOT trace to one of the three trusted roots, you never reach this line — you get SSLHandshakeException (cert path validation failure) earlier. Reaching this line means trust succeeded and the response is an ordinary non-2xx.
Source
Thrown at samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java:157
client = new OkHttpClient.Builder()
.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())
.build();
}
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()) {
Headers responseHeaders = response.headers();
for (int i = 0; i < responseHeaders.size(); i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
throw new IOException("Unexpected code " + response);
}
System.out.println(response.body().string());
}
}
public static void main(String... args) throws Exception {
new CustomTrust().run();
}
}
View on GitHub (pinned to 4fc0831380)
Solutions
- If you see SSLHandshakeException, uncomment .addPlatformTrustedCertificates() or add the missing root CA.
- If you actually reach this line, handle response.code() as a normal HTTP error.
- For production, load your trust anchors from a bundled PEM resource rather than hard-coding three CAs.
Example fix
// before
if (!response.isSuccessful()) {
// prints headers first, then throws
throw new IOException("Unexpected code " + response);
}
// after
if (!response.isSuccessful()) {
throw new IOException("HTTP " + response.code()
+ " (trust was OK; non-2xx from origin)");
}
// and if you are seeing handshake errors instead, enable platform trust:
// new HandshakeCertificates.Builder()
// .addPlatformTrustedCertificates()
// .addTrustedCertificate(myOrgRoot)
// .build(); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate that your trust store covers the target's chain // (otherwise you will get SSLHandshakeException, not the 'Unexpected code' line) // Use openssl s_client -connect host:443 -showcerts to see the chain issuer roots // and ensure each root is in your HandshakeCertificates.Builder.
Try / catch
try {
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new HttpException(response.code(), response.message());
} catch (SSLHandshakeException e) {
// chain does not trace to one of the three trusted roots
throw new TrustException("Untrusted chain — add the missing root or enable platform trust", e);
} Prevention
- Add .addPlatformTrustedCertificates() for general browsing, or list every root your endpoints use.
- Load PEMs from bundled resources, not hard-coded strings, for maintainability.
- Recognise that handshake failures (untrusted chain) are a different exception from the HTTP-status guard.
- Test against each endpoint after restricting the trust store.
When it happens
Trigger: GET https://publicobject.com/helloworld.txt with the three-CA trust store. Reaches this line when the cert chain IS trusted but the response is 4xx/5xx. If the chain were untrusted (e.g. site moved to a Google/AWS cert) the call fails at handshake with 'unable to find valid certification path to requested target'.
Common situations: Commenting out .addPlatformTrustedCertificates() (as shipped) then trying to reach sites whose chain is not one of the three CAs — that is a handshake failure, not this error; site legitimately returns 404/503; cert rotated to a chain outside the three.
Related errors
AI-assisted analysis of square/okhttp@4fc0831380 (2026-08-04).
Data as JSON: /data/errors/0bcc1be7d4c0e31f.json.
Report an issue: GitHub.