{"record":{"id":"0bcc1be7d4c0e31f","repo":"square/okhttp","slug":"unexpected-code-0bcc1b","errorCode":null,"errorMessage":"Unexpected code ","messagePattern":"Unexpected code ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java","lineNumber":157,"sourceCode":"\n    client = new OkHttpClient.Builder()\n            .sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())\n            .build();\n  }\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/helloworld.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) {\n        Headers responseHeaders = response.headers();\n        for (int i = 0; i < responseHeaders.size(); i++) {\n          System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n        }\n\n        throw new IOException(\"Unexpected code \" + response);\n      }\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CustomTrust().run();\n  }\n}\n","sourceCodeStart":139,"sourceCodeEnd":168,"githubUrl":"https://github.com/square/okhttp/blob/91a8b34c6f44bd28c421364f8edadc9f324dddd9/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java#L139-L168","documentation":"Thrown in the custom-trust-store recipe: `throw new IOException(\"Unexpected code \" + response)`. Unlike the other recipes, this one first prints all response headers THEN throws. The client trusts only the three embedded PEM root CAs (Comodo, Entrust, Let's Encrypt). If the server's chain is NOT anchored at one of those roots, you get an SSLPeerUnverifiedException during the handshake (earlier, different error). This IOException only fires when TLS succeeded but the HTTP status is non-2xx.","triggerScenarios":"https://publicobject.com/helloworld.txt presented a chain the custom trust manager accepted (Comodo/Entrust/Let's Encrypt root), but the server returned 404/403/5xx. Commenting out addPlatformTrustedCertificates means most OTHER sites will fail at the TLS layer instead.","commonSituations":"Sample host removed the file; pointing the sample at a host whose CA is not in the three embedded roots (-> SSL error, not this); forgetting to uncomment addPlatformTrustedCertificates when you need general HTTPS access.","solutions":["Check response.code() (printed headers in the sample help diagnose).","If you actually need broad HTTPS access, uncomment .addPlatformTrustedCertificates() in the builder.","Add the specific root CA for any host you want to reach that is not covered by the three embedded certs.","Distinguish SSLPeerUnverifiedException (wrong/missing trust root) from this HTTP-status IOException."],"exampleFix":"// before\nif (!response.isSuccessful()) {\n  Headers responseHeaders = response.headers();\n  for (int i = 0; i < responseHeaders.size(); i++) {\n    System.out.println(responseHeaders.name(i) + \": \" + responseHeaders.value(i));\n  }\n  throw new IOException(\"Unexpected code \" + response);\n}\n\n// after\nif (!response.isSuccessful()) {\n  throw new IOException(\"HTTP \" + response.code() + \" \" + response.message());\n}","handlingStrategy":"try-catch","validationCode":"// Ensure the target host's root CA is in the custom trust store before calling.\n// For broad access, include platform roots.\nHandshakeCertificates certs = new HandshakeCertificates.Builder()\n    .addPlatformTrustedCertificates() // uncomment for general HTTPS\n    .build();","typeGuard":"static boolean trustedBy(HandshakeCertificates certs, X509Certificate serverRoot) {\n  return Arrays.asList(certs.trustManager().getAcceptedIssuers()).contains(serverRoot);\n}","tryCatchPattern":"try {\n  // call\n} catch (SSLPeerUnverifiedException e) {\n  // server chain not anchored at one of your 3 embedded roots -> add the missing root\n} catch (IOException e) {\n  // includes 'Unexpected code' (HTTP status) when TLS succeeded\n}","preventionTips":["Distinguish SSLPeerUnverifiedException (missing root) from HTTP-status IOException.","Uncomment addPlatformTrustedCertificates() if you need general HTTPS access.","Add only the specific root CAs you need for your targets.","Inspect response.code() for the HTTP problem when TLS worked."],"tags":["okhttp","http-status","custom-trust","tls","java"],"backgroundTag":null,"analyzedSha":"91a8b34c6f44bd28c421364f8edadc9f324dddd9","analyzedAt":"2026-08-10T18:39:54.316Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}