Skip to content
ecdsa.com
Node.js tls / https / fetchNode.js crypto

Error message

unable to get local issuer certificate

Also printed as

  • code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'
  • UNABLE_TO_GET_ISSUER_CERT_LOCALLY

With fetch() the message is the cause of "TypeError: fetch failed"; npm reports the same condition as "npm error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY". If the server sends only its leaf, Node.js reports "unable to verify the first certificate" instead.

Node.js tls / https / fetch — code UNABLE_TO_GET_ISSUER_CERT_LOCALLY (OpenSSL X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, 20)

What it means

The server sent a chain (leaf and intermediate), but the certificate at its top was issued by a root that Node.js does not trust. By default Node.js trusts only the Mozilla root list compiled into the binary, not the operating system's store, so a company root installed on the machine is invisible to it unless you tell Node.js to use it.

Why it happens

How to fix it

  1. 1.

    NODE_EXTRA_CA_CERTS

    Points Node.js at a PEM file whose certificates are added to the built-in roots. It is read once when the process starts, so exporting it inside a running app has no effect.

    bash
    # Adds to Node's built-in roots; read once at start-up, so set it before node starts
    export NODE_EXTRA_CA_CERTS=/etc/ssl/private-ca/corp-root-ca.pem
    node app.js
  2. 2.

    Use the operating system's store

    --use-system-ca (or NODE_USE_SYSTEM_CA=1 in the environment) makes Node.js trust the certificates trusted by the OS, which is what IT departments manage.

    bash
    # Trust what the operating system trusts (Windows store, macOS Keychain, Linux bundle)
    node --use-system-ca app.js
    # or, for tools that start node themselves (npm scripts, CLIs):
    NODE_USE_SYSTEM_CA=1 npx some-cli
  3. 3.

    Set the default CA list in code

    tls.setDefaultCACertificates() replaces the default list for later connections made by https, tls and fetch; combine the existing defaults with your root as shown.

    js
    // In code, before the first request: keep the default roots and add yours.
    // Applies to https, tls and fetch. Available in current Node.js 22 and 24 releases.
    import tls from "node:tls";
    import fs from "node:fs";
    
    tls.setDefaultCACertificates([
      ...tls.getCACertificates("default"),
      fs.readFileSync("/etc/ssl/private-ca/corp-root-ca.pem", "utf8"),
    ]);
  4. 4.

    Trust the root for one agent

    The ca option of an agent or tls.connect() replaces the default roots for those connections, so include tls.rootCertificates if the same agent also talks to public hosts.

    js
    // Per-agent trust: note that "ca" REPLACES the default roots, so append them
    import https from "node:https";
    import tls from "node:tls";
    import fs from "node:fs";
    
    const agent = new https.Agent({ ca: [...tls.rootCertificates, fs.readFileSync("corp-root-ca.pem", "utf8")] });
    https.get("https://api.example.com/", { agent }, (res) => res.resume());

Related errors

Browse the full signature error database