{"id":"5fd4a849cb433425","repo":"square/okhttp","slug":"unexpected-code-5fd4a8","errorCode":null,"errorMessage":"Unexpected code ","messagePattern":"Unexpected code ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"samples/guide/src/main/java/okhttp3/recipes/CheckHandshake.java","lineNumber":55,"sourceCode":"        if (denylist.contains(pin)) {\n          throw new IOException(\"Denylisted peer certificate: \" + pin);\n        }\n      }\n      return chain.proceed(chain.request());\n    }\n  };\n\n  private final OkHttpClient client = new OkHttpClient.Builder()\n      .addNetworkInterceptor(CHECK_HANDSHAKE_INTERCEPTOR)\n      .build();\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()) throw new IOException(\"Unexpected code \" + response);\n\n      System.out.println(response.body().string());\n    }\n  }\n\n  public static void main(String... args) throws Exception {\n    new CheckHandshake().run();\n  }\n}\n","sourceCodeStart":37,"sourceCodeEnd":65,"githubUrl":"https://github.com/square/okhttp/blob/4fc083138014aba3d0078f5c26d1ce84815fa984/samples/guide/src/main/java/okhttp3/recipes/CheckHandshake.java#L37-L65","documentation":"Recipe-level guard on the GET inside CheckHandshake. Because the denylist interceptor (error [6]) runs first, reaching this line means the peer certificate was NOT denylisted and chain.proceed() completed. If the resulting Response is non-2xx, java.io.IOException(\"Unexpected code \" + response) is thrown. This is independent of the handshake check.","triggerScenarios":"GET https://publicobject.com/helloworld.txt after the handshake interceptor passed. Fires for ordinary non-2xx (404, 503, etc.). A denylisted cert would have thrown at the interceptor, never reaching here.","commonSituations":"Conflating this line with the denylist error above; the origin returning 5xx while users blame the handshake interceptor.","solutions":["Inspect response.code() — it is a normal HTTP status, unrelated to certificate checking.","Keep denylist handling (error [6]) and HTTP-status handling (this line) in separate catch branches."],"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    throw new IOException(\"HTTP \" + response.code() + \" (handshake passed, denylist OK)\");\n  }\n  ...\n}","handlingStrategy":"validation","validationCode":"// Separate HTTP-status handling from the handshake/denylist policy\n// After the call returns (handshake + denylist passed), branch on code:\ntry (Response response = client.newCall(request).execute()) {\n  if (!response.isSuccessful()) {\n    errors.accept(new HttpException(response.code(), response.request().url().toString()));\n    return;\n  }\n  // use body\n}","typeGuard":null,"tryCatchPattern":"try (Response response = client.newCall(request).execute()) {\n  if (!response.isSuccessful()) {\n    // handshake passed AND denylist passed; this is a plain HTTP error\n    throw new HttpException(response.code(), response.message());\n  }\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"Denylisted\")) {\n    throw new SecurityPolicyException(e);\n  }\n  throw e;\n}","preventionTips":["Keep denylist policy handling and HTTP-status handling in distinct branches.","Do not conflate reaching this line with a handshake problem.","Inspect response.code() to determine the actual HTTP failure."],"tags":["okhttp","http-status","java"],"analyzedSha":"4fc083138014aba3d0078f5c26d1ce84815fa984","analyzedAt":"2026-08-04T19:09:04.639Z","schemaVersion":2}