usebruno/bruno · error · Error

Unable to load custom CA certificate: ${(err as Error).messa

Error message

Unable to load custom CA certificate: ${(err as Error).message}

What it means

getCACertificates fails to read an existing custom CA file. fs.existsSync returned true but fs.readFileSync threw (permissions, EISDIR, EACCES, encoding error), so the loader re-throws wrapped with the underlying message.

Source

Thrown at packages/bruno-requests/src/utils/ca-cert.ts:119

    let systemCerts: string[] = [];
    let rootCerts: string[] = [];
    let customCerts: string[] = [];
    let nodeExtraCerts: string[] = [];

    // handle user-provided custom CA certificate file with optional default certificates
    if (caCertFilePath) {
      // validate custom CA certificate file
      if (fs.existsSync(caCertFilePath)) {
        try {
          const customCert = fs.readFileSync(caCertFilePath, 'utf8');
          if (customCert && customCert.trim()) {
            customCerts.push(customCert);
            caCertificatesCount.custom = customCerts.length;
          }
        } catch (err) {
          console.error(`Failed to read custom CA certificate from ${caCertFilePath}:`, (err as Error).message);
          throw new Error(`Unable to load custom CA certificate: ${(err as Error).message}`);
        }
      } else {
        throw new Error(`Invalid custom CA certificate path: ${caCertFilePath}`);
      }

      if (shouldKeepDefaultCerts) {
        // get system certs
        systemCerts = getSystemCerts();
        caCertificatesCount.system = systemCerts.length;

        // get root certs
        rootCerts = [...tls.rootCertificates];
        caCertificatesCount.root = rootCerts.length;
      }
    } else {
      // get system certs
      systemCerts = getSystemCerts();
      caCertificatesCount.system = systemCerts.length;

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Confirm the path is a regular file: fs.statSync(caCertFilePath).isFile().
  2. Fix permissions (chmod 644 / take ownership) so the process can read it.
  3. Point caCertFilePath to a real .pem/.crt file rather than a directory.

Example fix

// before
{ caCertFilePath: '/etc/ssl/certs' } // directory -> EISDIR -> throws

// after
{ caCertFilePath: '/etc/ssl/certs/myCA.pem' }
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
function assertReadableCertFile(p) {
  if (!p || !fs.existsSync(p)) throw new Error(`Invalid custom CA certificate path: ${p}`);
  const st = fs.statSync(p);
  if (!st.isFile()) throw new Error(`Unable to load custom CA certificate: not a file (${p})`);
  fs.accessSync(p, fs.constants.R_OK);
}

Type guard

function isReadableFile(p): p is string { try { return typeof p==='string' && !!p && fs.statSync(p).isFile() && fs.accessSync(p, fs.constants.R_OK) === undefined; } catch { return false; } }

Try / catch

try { return getCACertificates({ caCertFilePath }); } catch (e) { if (/Unable to load custom CA certificate/.test(e.message)) { /* disable custom CA, retry with defaults */ } else throw e; }

Prevention

When it happens

Trigger: caCertFilePath points to a directory or a special file, the process lacks read permission, the file was deleted between the existsSync and readFileSync calls (TOCTOU), or the file is not valid UTF-8.

Common situations: Path is a directory, a symlink to a non-readable target, on a network mount with intermittent access, or owned by root while bruno runs as a normal user.

Understand the failure class

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/ddd21ce926ccf5ed. Report an issue: GitHub.