{"record":{"id":"031a7606581427ed","repo":"oven-sh/bun","slug":"unable-to-get-issuer-cert","errorCode":null,"errorMessage":"UNABLE_TO_GET_ISSUER_CERT","messagePattern":"UNABLE_TO_GET_ISSUER_CERT","errorType":"error_code","errorClass":"CertError","httpStatus":null,"severity":"error","filePath":"src/http/error.rs","lineNumber":125,"sourceCode":"    Core(#[from] bun_core::Error),\n    #[error(transparent)]\n    Sys(#[from] bun_errno::SystemErrno),\n    #[error(transparent)]\n    Zlib(bun_zlib::ZlibError),\n    #[error(transparent)]\n    Brotli(bun_brotli::Error),\n    #[error(transparent)]\n    Zstd(bun_zstd::ZstdError),\n    #[error(transparent)]\n    Picohttp(bun_picohttp::ParseResponseError),\n}\n\n#[allow(non_camel_case_types)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error, strum::IntoStaticStr)]\npub enum CertError {\n    #[error(\"OK\")]\n    OK,\n    #[error(\"UNABLE_TO_GET_ISSUER_CERT\")]\n    UNABLE_TO_GET_ISSUER_CERT,\n    #[error(\"UNABLE_TO_GET_CRL\")]\n    UNABLE_TO_GET_CRL,\n    #[error(\"UNABLE_TO_DECRYPT_CERT_SIGNATURE\")]\n    UNABLE_TO_DECRYPT_CERT_SIGNATURE,\n    #[error(\"UNABLE_TO_DECRYPT_CRL_SIGNATURE\")]\n    UNABLE_TO_DECRYPT_CRL_SIGNATURE,\n    #[error(\"UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY\")]\n    UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,\n    #[error(\"CERT_SIGNATURE_FAILURE\")]\n    CERT_SIGNATURE_FAILURE,\n    #[error(\"CRL_SIGNATURE_FAILURE\")]\n    CRL_SIGNATURE_FAILURE,\n    #[error(\"CERT_NOT_YET_VALID\")]\n    CERT_NOT_YET_VALID,\n    #[error(\"CERT_HAS_EXPIRED\")]\n    CERT_HAS_EXPIRED,\n    #[error(\"CRL_NOT_YET_VALID\")]","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/http/error.rs#L107-L143","documentation":"BoringSSL X509 verify result 2 (X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT): during the TLS handshake the verifier could not find the certificate of the issuer of a certificate in the chain, so the chain cannot be built to a trusted root. Bun maps the numeric code via get_cert_error_from_no (src/http/lib.rs:1521) into http::Error::Cert(CertError::UNABLE_TO_GET_ISSUER_CERT) and rejects the request with code \"UNABLE_TO_GET_ISSUER_CERT\" and message \"unable to get issuer certificate\" (src/runtime/webcore/fetch/FetchTasklet.rs:1376).","triggerScenarios":"fetch(\"https://host\") or Bun.connect with TLS (or an HTTPS request tunneled through an HTTP proxy, src/http/ProxyTunnel.rs:374) where reject_unauthorized is true (default) and the server presents a chain missing an intermediate CA certificate, or the intermediate is not in Bun's CA root store.","commonSituations":"Server admins who serve only the leaf cert (nginx misconfiguration using ssl_certificate with only the server.pem instead of fullchain.pem), freshly rotated certificates, private/internal PKIs whose intermediates were never distributed, or Docker images that trimmed the CA store.","solutions":["Fix the server to serve the full chain (e.g. nginx: ssl_certificate fullchain.pem; certbot deployments already produce fullchain.pem)","Pass the missing intermediate(s) to the client: fetch(url, { tls: { ca: [intermediatePem] } }) or set NODE_EXTRA_CA_CERTS=/path/to/intermediates.pem","If the issuer is a private root, add that root via tls.ca or NODE_EXTRA_CA_CERTS","As a last resort for internal services, fetch(url, { tls: { rejectUnauthorized: false } }) — never for public traffic"],"exampleFix":"# before (server serves only the leaf):\nssl_certificate /etc/letsencrypt/live/example.com/cert.pem;\n\n# after (serve the full chain):\nssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isCertErrorCode(e: unknown, code = \"UNABLE_TO_GET_ISSUER_CERT\"): e is Error & { code: string } {\n  return e instanceof Error && (e as any).code === code;\n}","tryCatchPattern":"try {\n  const res = await fetch(\"https://api.example.com\");\n} catch (e) {\n  if (isCertErrorCode(e, \"UNABLE_TO_GET_ISSUER_CERT\")) {\n    // chain incomplete: alert the service owner, or retry with known intermediates pinned\n    const res = await fetch(\"https://api.example.com\", { tls: { ca: intermediatePems } });\n  } else throw e;\n}","preventionTips":["Serve fullchain.pem (leaf + intermediates) on every TLS endpoint","Monitor cert chains with openssl s_client in CI against your own endpoints","Pin known intermediates via tls.ca for third-party APIs with a history of chain issues"],"tags":["tls","certificates","fetch","network","pkix"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}