oven-sh/bun · error · CertError
CERT_UNTRUSTED
CERT_UNTRUSTED
Error message
CERT_UNTRUSTED
What it means
CertError::CERT_UNTRUSTED maps BoringSSL X509 verify code 27 (X509_V_ERR_CERT_UNTRUSTED) via get_cert_error_from_no() in src/http/lib.rs:1546. The chain built completely to a root, but that root (or an intermediate treated as anchor) is not present in the trust store used by the connection. JS error.code is CERT_UNTRUSTED with message 'certificate not trusted' (FetchTasklet.rs:1460).
Source
Thrown at src/http/error.rs:175
#[error("DEPTH_ZERO_SELF_SIGNED_CERT")]
DEPTH_ZERO_SELF_SIGNED_CERT,
#[error("SELF_SIGNED_CERT_IN_CHAIN")]
SELF_SIGNED_CERT_IN_CHAIN,
#[error("UNABLE_TO_GET_ISSUER_CERT_LOCALLY")]
UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
#[error("UNABLE_TO_VERIFY_LEAF_SIGNATURE")]
UNABLE_TO_VERIFY_LEAF_SIGNATURE,
#[error("CERT_CHAIN_TOO_LONG")]
CERT_CHAIN_TOO_LONG,
#[error("CERT_REVOKED")]
CERT_REVOKED,
#[error("INVALID_CA")]
INVALID_CA,
#[error("PATH_LENGTH_EXCEEDED")]
PATH_LENGTH_EXCEEDED,
#[error("INVALID_PURPOSE")]
INVALID_PURPOSE,
#[error("CERT_UNTRUSTED")]
CERT_UNTRUSTED,
#[error("CERT_REJECTED")]
CERT_REJECTED,
#[error("SUBJECT_ISSUER_MISMATCH")]
SUBJECT_ISSUER_MISMATCH,
#[error("AKID_SKID_MISMATCH")]
AKID_SKID_MISMATCH,
#[error("AKID_ISSUER_SERIAL_MISMATCH")]
AKID_ISSUER_SERIAL_MISMATCH,
#[error("KEYUSAGE_NO_CERTSIGN")]
KEYUSAGE_NO_CERTSIGN,
#[error("UNABLE_TO_GET_CRL_ISSUER")]
UNABLE_TO_GET_CRL_ISSUER,
#[error("UNHANDLED_CRITICAL_EXTENSION")]
UNHANDLED_CRITICAL_EXTENSION,
#[error("KEYUSAGE_NO_CRL_SIGN")]
KEYUSAGE_NO_CRL_SIGN,
#[error("UNHANDLED_CRITICAL_CRL_EXTENSION")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Export the CA root that actually terminates the chain (openssl s_client -showcerts, last cert) and pass it: fetch(url, { tls: { ca: await Bun.file('corp-root.pem').text() })
- Or set NODE_EXTRA_CA_CERTS=/path/to/corp-root.pem for the process
- If behind a TLS-inspection proxy, install the proxy's root CA bundle system-wide (update-ca-certificates)
- Confirm the anchor: openssl verify -CAfile corp-root.pem leaf.pem
Example fix
// before
await fetch("https://svc.corp.internal/api");
// after
const ca = await Bun.file("/certs/corp-root.pem").text();
await fetch("https://svc.corp.internal/api", { tls: { ca } }); Defensive patterns
Strategy: try-catch
Validate before calling
import tls from "node:tls";
function endpointTrusted(host: string, ca: string, port = 443): Promise<boolean> {
return new Promise((resolve) => {
const s = tls.connect({ host, port, servername: host, ca }, () => { resolve(s.authorized); s.destroy(); });
s.on("error", () => resolve(false));
});
}
const ca = await Bun.file("corp-root.pem").text();
if (!(await endpointTrusted("svc.corp.internal", ca))) throw new Error("corp root does not trust endpoint");
await fetch("https://svc.corp.internal/api", { tls: { ca } }); Type guard
function isCertUntrusted(e: unknown): e is Error & { code: "CERT_UNTRUSTED" } {
return e instanceof Error && (e as any).code === "CERT_UNTRUSTED";
} Try / catch
try { await fetch(url, { tls: { ca } }); } catch (e) { if (isCertUntrusted(e)) { logger.error("missing corporate CA — set NODE_EXTRA_CA_CERTS"); process.exitCode = 1; } throw e; } Prevention
- Bake corporate CA bundles into docker/CI images (update-ca-certificates + NODE_EXTRA_CA_CERTS)
- Pre-flight tls.connect probes after env changes
- Fail loudly on trust errors instead of disabling verification
When it happens
Trigger: fetch()/Bun.connect to a host whose TLS chain terminates in a private/corporate CA that was never added via tls: { ca } or NODE_EXTRA_CA_CERTS; corporate TLS-inspecting proxy (Zscaler, Netskope) re-signing traffic with its own root.
Common situations: Corporate laptops behind SSL-inspection proxies, internal *.corp domains served by a private CA, CI containers missing the company root, docker images that don't COPY the corporate CA bundle.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- CERT_REJECTED
- SELF_SIGNED_CERT_IN_CHAIN
- UNABLE_TO_GET_ISSUER_CERT_LOCALLY
- UNABLE_TO_VERIFY_LEAF_SIGNATURE
- CERT_CHAIN_TOO_LONG
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/821e0b78e5b8c523.
Report an issue: GitHub.