{"record":{"id":"821e0b78e5b8c523","repo":"oven-sh/bun","slug":"cert-untrusted","errorCode":"CERT_UNTRUSTED","errorMessage":"CERT_UNTRUSTED","messagePattern":"CERT_UNTRUSTED","errorType":"error_code","errorClass":"CertError","httpStatus":null,"severity":"error","filePath":"src/http/error.rs","lineNumber":175,"sourceCode":"    #[error(\"DEPTH_ZERO_SELF_SIGNED_CERT\")]\n    DEPTH_ZERO_SELF_SIGNED_CERT,\n    #[error(\"SELF_SIGNED_CERT_IN_CHAIN\")]\n    SELF_SIGNED_CERT_IN_CHAIN,\n    #[error(\"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\")]\n    UNABLE_TO_GET_ISSUER_CERT_LOCALLY,\n    #[error(\"UNABLE_TO_VERIFY_LEAF_SIGNATURE\")]\n    UNABLE_TO_VERIFY_LEAF_SIGNATURE,\n    #[error(\"CERT_CHAIN_TOO_LONG\")]\n    CERT_CHAIN_TOO_LONG,\n    #[error(\"CERT_REVOKED\")]\n    CERT_REVOKED,\n    #[error(\"INVALID_CA\")]\n    INVALID_CA,\n    #[error(\"PATH_LENGTH_EXCEEDED\")]\n    PATH_LENGTH_EXCEEDED,\n    #[error(\"INVALID_PURPOSE\")]\n    INVALID_PURPOSE,\n    #[error(\"CERT_UNTRUSTED\")]\n    CERT_UNTRUSTED,\n    #[error(\"CERT_REJECTED\")]\n    CERT_REJECTED,\n    #[error(\"SUBJECT_ISSUER_MISMATCH\")]\n    SUBJECT_ISSUER_MISMATCH,\n    #[error(\"AKID_SKID_MISMATCH\")]\n    AKID_SKID_MISMATCH,\n    #[error(\"AKID_ISSUER_SERIAL_MISMATCH\")]\n    AKID_ISSUER_SERIAL_MISMATCH,\n    #[error(\"KEYUSAGE_NO_CERTSIGN\")]\n    KEYUSAGE_NO_CERTSIGN,\n    #[error(\"UNABLE_TO_GET_CRL_ISSUER\")]\n    UNABLE_TO_GET_CRL_ISSUER,\n    #[error(\"UNHANDLED_CRITICAL_EXTENSION\")]\n    UNHANDLED_CRITICAL_EXTENSION,\n    #[error(\"KEYUSAGE_NO_CRL_SIGN\")]\n    KEYUSAGE_NO_CRL_SIGN,\n    #[error(\"UNHANDLED_CRITICAL_CRL_EXTENSION\")]","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/http/error.rs#L157-L193","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait fetch(\"https://svc.corp.internal/api\");\n// after\nconst ca = await Bun.file(\"/certs/corp-root.pem\").text();\nawait fetch(\"https://svc.corp.internal/api\", { tls: { ca } });","handlingStrategy":"try-catch","validationCode":"import tls from \"node:tls\";\nfunction endpointTrusted(host: string, ca: string, port = 443): Promise<boolean> {\n  return new Promise((resolve) => {\n    const s = tls.connect({ host, port, servername: host, ca }, () => { resolve(s.authorized); s.destroy(); });\n    s.on(\"error\", () => resolve(false));\n  });\n}\nconst ca = await Bun.file(\"corp-root.pem\").text();\nif (!(await endpointTrusted(\"svc.corp.internal\", ca))) throw new Error(\"corp root does not trust endpoint\");\nawait fetch(\"https://svc.corp.internal/api\", { tls: { ca } });","typeGuard":"function isCertUntrusted(e: unknown): e is Error & { code: \"CERT_UNTRUSTED\" } {\n  return e instanceof Error && (e as any).code === \"CERT_UNTRUSTED\";\n}","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["tls","certificate","trust-store","fetch","corporate-proxy"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}