apple/pkl · error · HttpClientException

cannotReadCertFile

cannotReadCertFile

Error message

cannotReadCertFile: ${reason}

What it means

Pkl found a configured certificate file but could not read its contents, typically due to an I/O problem (permissions, transient OS error). gatherCertificates catches the IOException and surfaces the root reason via cannotReadCertFile. Unlike error 100, the file exists but reading it failed.

Solutions

  1. Check and fix file permissions so the user running Pkl can read the certificate file (e.g. `chmod 644` or add the user to the right group).
  2. Read the `${reason}` in the message to identify the root cause; fix that underlying OS/IO issue.
  3. Verify the file is on a mounted, available filesystem (not an unmounted volume).
  4. If running in a container, ensure the cert file is copied/mounted with readable permissions.

Example fix

// before (shell)
-rw------- root root /etc/ssl/certs/custom-ca.pem   # unreadable by app user
// after (shell)
chown root:app /etc/ssl/certs/custom-ca.pem && chmod 640 /etc/ssl/certs/custom-ca.pem
Defensive patterns

Strategy: validation

Validate before calling

Path certFile = Paths.get(caCertPath);
try (InputStream in = Files.newInputStream(certFile)) {
  in.read(); // forces an actual read to surface permission/IO issues early
} catch (IOException e) {
  throw new IllegalStateException("Cannot read cert file: " + e.getMessage(), e);
}

Try / catch

try {
  // use the HTTP client
} catch (HttpClientException e) {
  if (e.getMessage().startsWith("cannotReadCertFile")) {
    // check file permissions / fallback to system trust store
  }
}

Prevention

When it happens

Trigger: Files.newInputStream succeeds in opening or reading a configured certificate file but an IOException occurs; Exceptions.getRootReason(e) supplies the `${reason}`. Typical: unreadable file permissions, read-protected file, or hardware/OS read failure during gatherCertificates.

Common situations: Certificate file owned by root with 0600 while Pkl runs as another user, file unreadable inside a container, SELinux/AppArmor denial, or an NFS/network mount being unavailable.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/6359e664c563ab9d. Report an issue: GitHub.

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java:164

      sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());

      return sslContext;
    } catch (GeneralSecurityException | IOException e) {
      throw new HttpClientException(
          ErrorMessages.create("cannotInitHttpClient", Exceptions.getRootReason(e)), e);
    }
  }

  private static List<Certificate> gatherCertificates(
      CertificateFactory factory, List<Path> certificateFiles, List<ByteBuffer> certificateBytes) {
    var certificates = new ArrayList<Certificate>();
    for (var file : certificateFiles) {
      try (var stream = Files.newInputStream(file)) {
        collectCertificates(certificates, factory, stream, file);
      } catch (NoSuchFileException e) {
        throw new HttpClientException(ErrorMessages.create("cannotFindCertFile", file));
      } catch (IOException e) {
        throw new HttpClientException(
            ErrorMessages.create("cannotReadCertFile", Exceptions.getRootReason(e)));
      }
    }
    for (var byteBuffer : certificateBytes) {
      var stream = new ByteArrayInputStream(byteBuffer.array());
      collectCertificates(certificates, factory, stream, "<unavailable>");
    }
    return certificates;
  }

  private static void collectCertificates(
      ArrayList<Certificate> anchors,
      CertificateFactory factory,
      InputStream stream,
      Object source) {
    var input = new PushbackInputStream(stream);

    try {

View on GitHub (pinned to f3efcbfc9b)