{"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/4fc083138014aba3d0078f5c26d1ce84815fa984/samples/guide/src/main/java/okhttp3/recipes/CertificatePinning.java#L21-L51","documentation":"Recipe-level guard after a GET that uses certificate pinning. The client is built with a CertificatePinner that pins publicobject.com to a single sha256 SPKI pin. Note: a pin MISMATCH does NOT reach this line — it fails earlier as SSLPeerUnverifiedException/SSLHandshakeException during the handshake. This 'Unexpected code' therefore means the TLS handshake succeeded (pin matched) but the HTTP response was non-2xx.","triggerScenarios":"GET https://publicobject.com/robots.txt with a pinned cert. Reaches this line only when the handshake passed and the server returned a non-2xx (e.g. 404 for /robots.txt, 503). If the pin were wrong you would get a different exception before execute() returned.","commonSituations":"Treating this error as a pin problem (it is not — pin failures throw earlier); the pinned cert rotated and now the handshake fails with SSLHandshakeException, which users misattribute to this line; /robots.txt genuinely missing.","solutions":["If you are seeing SSLPeerUnverifiedException, the pin is stale — recompute the pin by printing CertificatePinner.pin(certificate) for the current cert and update the CertificatePinner.","If you are actually reaching this line, inspect response.code() — it is an ordinary HTTP error, not a pinning issue.","Pin a backup hash (multiple .add() entries) so cert rotation does not break the app."],"exampleFix":"// before\ntry (Response response = client.newCall(request).execute()) {\n  if (!response.isSuccessful()) throw new IOException(\"Unexpected code \" + response);\n  ...\n}\n\n// after\ntry (Response response = client.newCall(request).execute()) {\n  if (!response.isSuccessful()) {\n    // handshake already succeeded, so this is a plain HTTP error, not a pin failure\n    throw new IOException(\"HTTP \" + response.code() + \" (pin was OK)\");\n  }\n  ...\n}\n// and pin a backup so rotation does not break you:\n//   .add(\"publicobject.com\", \"sha256/<primary>\")\n//   .add(\"publicobject.com\", \"sha256/<backup>\")","handlingStrategy":"try-catch","validationCode":"// Pre-validate that your pin set still matches the current cert\n// Run this once to capture the current SPKI hashes, then pin them\ntry (Response r = new OkHttpClient().newCall(\n        new Request.Builder().url(\"https://publicobject.com/robots.txt\").build()).execute()) {\n  for (Certificate c : r.handshake().peerCertificates()) {\n    System.out.println(CertificatePinner.pin(c));\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  Response response = client.newCall(request).execute();\n} catch (SSLPeerUnverifiedException | SSLHandshakeException e) {\n  // pin mismatch (or untrusted cert) — happens BEFORE the 'Unexpected code' line\n  throw new PinMismatchException(\"Certificate pin no longer matches\", e);\n} catch (IOException e) {\n  // reached the response guard: handshake was fine, this is an HTTP error\n  if (e.getMessage().startsWith(\"Unexpected code\")) { /* plain HTTP error */ }\n}","preventionTips":["Pin at least two SPKI hashes (primary + backup) so cert rotation does not break clients.","Recognise that pin failures throw SSLHandshakeException earlier — the 'Unexpected code' line means the pin matched.","Recompute pins after the server rotates its certificate.","Avoid pinning leaf certs; prefer intermediate/SPKI pins."],"tags":["okhttp","http-status","certificate-pinning","tls","java"],"analyzedSha":"4fc083138014aba3d0078f5c26d1ce84815fa984","analyzedAt":"2026-08-04T19:09:04.639Z","schemaVersion":2}