{"record":{"id":"090fb7b6d0a39e46","repo":"apple/pkl","slug":"errorsslhandshake","errorCode":"errorSslHandshake","errorMessage":"errorSslHandshake: ${host}: ${reason}","messagePattern":"errorSslHandshake: (.+?): (.+?)","errorType":"exception","errorClass":"SSLHandshakeException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java","lineNumber":103,"sourceCode":"            .proxy(proxySelector)\n            .followRedirects(Redirect.NEVER)\n            .build();\n  }\n\n  @Override\n  public <T> HttpResponse<T> send(\n      HttpRequest request,\n      BodyHandler<T> responseBodyHandler,\n      HttpRequestChecker httpRequestChecker)\n      throws IOException {\n    try {\n      return underlying.send(request, responseBodyHandler);\n    } catch (ConnectException e) {\n      // original exception has no message\n      throw new ConnectException(\n          ErrorMessages.create(\"errorConnectingToHost\", request.uri().getHost()));\n    } catch (SSLHandshakeException e) {\n      throw new SSLHandshakeException(\n          ErrorMessages.create(\n              \"errorSslHandshake\", request.uri().getHost(), Exceptions.getRootReason(e)));\n    } catch (SSLException e) {\n      throw new SSLException(Exceptions.getRootReason(e));\n    } catch (InterruptedException e) {\n      // next best thing after letting (checked) InterruptedException bubble up\n      Thread.currentThread().interrupt();\n      throw new IOException(e);\n    }\n  }\n\n  @Override\n  public void close() {\n    try {\n      closeMethod.invoke(underlying);\n    } catch (RuntimeException | Error e) {\n      throw e;\n    } catch (Throwable t) {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java#L85-L121","documentation":"JdkHttpClient.send catches SSLHandshakeException and rethrows an SSLHandshakeException with code 'errorSslHandshake' including the host and the root cause reason. It means the TLS handshake with the server failed — typically certificate trust, protocol, or cipher problems.","triggerScenarios":"Any HTTPS request where the TLS handshake fails: self-signed or unknown CA certificate, expired server certificate, hostname mismatch, TLS version/cipher incompatibility, corporate TLS-intercepting proxy.","commonSituations":"Internal CA not in the trust store, MITM proxies at work, dev servers with self-signed certs, custom certificates not passed via evaluator settings (certificateFiles/certificateBytes).","solutions":["Add the server's (or corporate CA's) certificate to the evaluator's trusted certificates via PklEvaluatorSettings certificateFiles/certificateBytes","Read the 'reason' in the message: fix expired/mismatched certificates on the server if you control it","If behind a TLS-intercepting proxy, import the proxy's root CA into the trust settings","Check TLS version compatibility (force TLS 1.2/1.3 as needed)"],"exampleFix":"// before: evaluator without custom CA, self-signed internal host fails\nvar eval = EvaluatorBuilder.preconfigured().build();\n// after\nvar settings = new PklEvaluatorSettings();\nsettings.setCertificateFiles(List.of(Path.of(\"/etc/certs/internal-ca.pem\")));\nvar eval = EvaluatorBuilder.preconfigured().applyFromSettings(settings).build();","handlingStrategy":"fallback","validationCode":"// verify the server certificate is trusted before evaluating\nProcess p = new ProcessBuilder(\"openssl\", \"s_client\", \"-connect\", host + \":443\", \"-servername\", host).start();\n// inspect the presented chain; ensure the CA is in your evaluator's certificateFiles","typeGuard":null,"tryCatchPattern":"try {\n  return client.send(request, handler);\n} catch (SSLHandshakeException e) {\n  if (e.getMessage().startsWith(\"errorSslHandshake\")) {\n    // rebuild client with the CA from the message's reason (e.g. add CA cert to settings) and retry once\n  }\n}","preventionTips":["Distribute your organization's root CA via PklEvaluatorSettings certificateFiles/certificateBytes","Monitor certificate expiry for internal registries your projects import from","Test TLS connectivity from the same environment where evaluations run","Account for TLS-intercepting proxies by trusting their root CA explicitly"],"tags":["ssl","tls","certificate","network"],"backgroundTag":"ssl-handshake-failed","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}