{"record":{"id":"70c1829c73e7c085","repo":"square/okhttp","slug":"unexpected-code-70c182","errorCode":null,"errorMessage":"Unexpected code ","messagePattern":"Unexpected code ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"samples/guide/src/main/java/okhttp3/recipes/CertificatePinning.java","lineNumber":39,"sourceCode":"import okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.Response;\n\npublic final class CertificatePinning {\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .certificatePinner(\n          new CertificatePinner.Builder()\n              .add(\"publicobject.com\", \"sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=\")\n              .build())\n      .build();\n\n  public void run() throws Exception {\n    Request request = new Request.Builder()\n        .url(\"https://publicobject.com/robots.txt\")\n        .build();\n\n    try (Response response = client.newCall(request).execute()) {\n      if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n      for (Certificate certificate : response.handshake().peerCertificates()) {\n        System.out.println(CertificatePinner.pin(certificate));\n      }\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CertificatePinning().run();\n  }\n}\n","sourceCodeStart":21,"sourceCodeEnd":51,"githubUrl":"https://github.com/square/okhttp/blob/91a8b34c6f44bd28c421364f8edadc9f324dddd9/samples/guide/src/main/java/okhttp3/recipes/CertificatePinning.java#L21-L51","documentation":"Thrown in the certificate-pinning recipe: `if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response)`. Pinning validates the server's certificate chain against the hardcoded SHA-256 pin; if pinning fails OkHttp throws SSLPeerUnverifiedException during the handshake (a transport error -> onFailure / execute IOException), NOT this guard. This particular line only fires when the handshake SUCCEEDED (pin matched) but the HTTP status is non-2xx.","triggerScenarios":"Handshake succeeds against https://publicobject.com/robots.txt (pin matched) but the server returns 404 (no robots.txt), 403, or 5xx. Note: a pin mismatch surfaces earlier as SSLPeerUnverifiedException and never reaches this line.","commonSituations":"The pinned host removed /robots.txt; the pin in the sample is stale and the server rotated its certificate (would fail at handshake, not here, but is commonly confused); copy-pasting the sample pin against a different host.","solutions":["Distinguish the two failure classes: handshake/SSL errors mean a pin problem; this line means an HTTP status problem.","Check response.code() to see the real status; 404 means the path is missing, not a TLS issue.","Verify the pinned certificate is still current by reading peerCertificates from a successful response.","Use a branch instead of a blanket throw so pinning demos and status errors are reported separately."],"exampleFix":"// before\nif (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n\n// after\nif (!response.isSuccessful()) {\n  throw new IOException(\"HTTP \" + response.code()\n      + \" (handshake OK, pin=\" + CertificatePinner.pin(response.handshake().peerCertificates().get(0)) + \")\");\n}","handlingStrategy":"try-catch","validationCode":"// Separate TLS errors from HTTP-status errors.\ntry (Response r = client.newCall(request).execute()) {\n  if (!r.isSuccessful()) { /* HTTP status problem, not pinning */ }\n} catch (SSLPeerUnverifiedException e) {\n  // THIS is a pin mismatch; HTTP status never reached.\n}","typeGuard":"static boolean pinMatches(Response r, String expectedPin) {\n  return r.handshake() != null\n    && CertificatePinner.pin(r.handshake().peerCertificates().get(0)).equals(expectedPin);\n}","tryCatchPattern":"try {\n  // call\n} catch (SSLPeerUnverifiedException e) {\n  // certificate pin mismatch (TLS layer)\n} catch (IOException e) {\n  // includes 'Unexpected code' (HTTP status) and other transport errors\n}","preventionTips":["Distinguish SSLPeerUnverifiedException (pin/TLS) from HTTP-status IOException.","Keep certificate pins current; rotate them when servers renew certs.","Hold multiple pins (old + new) during certificate rotation.","Derive pins from real peer certs, not from sample strings."],"tags":["okhttp","http-status","certificate-pinning","tls","java"],"backgroundTag":null,"analyzedSha":"91a8b34c6f44bd28c421364f8edadc9f324dddd9","analyzedAt":"2026-08-10T18:39:54.316Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}