oven-sh/bun · error · CertError
UNABLE_TO_VERIFY_LEAF_SIGNATURE
UNABLE_TO_VERIFY_LEAF_SIGNATURE
Error message
UNABLE_TO_VERIFY_LEAF_SIGNATURE
What it means
CertError::UNABLE_TO_VERIFY_LEAF_SIGNATURE maps BoringSSL X509 verify code 21 (X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) via get_cert_error_from_no() in src/http/lib.rs:1540. It means the TLS handshake reached certificate verification and the signature on the first (leaf) certificate could not be verified against the issuer candidate the verifier picked. JS code exposes the tag as error.code with message 'unable to verify the first certificate' (FetchTasklet.rs:1431).
Source
Thrown at src/http/error.rs:163
#[error("CRL_HAS_EXPIRED")]
CRL_HAS_EXPIRED,
#[error("ERROR_IN_CERT_NOT_BEFORE_FIELD")]
ERROR_IN_CERT_NOT_BEFORE_FIELD,
#[error("ERROR_IN_CERT_NOT_AFTER_FIELD")]
ERROR_IN_CERT_NOT_AFTER_FIELD,
#[error("ERROR_IN_CRL_LAST_UPDATE_FIELD")]
ERROR_IN_CRL_LAST_UPDATE_FIELD,
#[error("ERROR_IN_CRL_NEXT_UPDATE_FIELD")]
ERROR_IN_CRL_NEXT_UPDATE_FIELD,
#[error("OUT_OF_MEM")]
OUT_OF_MEM,
#[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")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Run openssl s_client -connect host:443 -showcerts and confirm the leaf's issuer DN matches the subject DN AND public key of the next cert served
- Fix the server's fullchain file (leaf first, then the correct intermediate) — usually certbot --nginx or ssl_certificate pointing at fullchain.pem instead of cert.pem
- If the chain is intentionally private, pass the matching intermediate: fetch(url, { tls: { ca: intermediatePem } })
- Check for re-keyed intermediates: verify the leaf's Authority Key Identifier equals the intermediate's Subject Key Identifier (openssl x509 -text)
Example fix
// before: server serves leaf only
fetch("https://internal.example.com/v1");
// after: client supplies the matching intermediate
const intermediate = await Bun.file("intermediate.pem").text();
await fetch("https://internal.example.com/v1", {
tls: { ca: intermediate },
}); Defensive patterns
Strategy: try-catch
Validate before calling
import tls from "node:tls";
async function chainVerifies(host: string, port = 443): Promise<boolean> {
return new Promise((resolve) => {
const s = tls.connect({ host, port, servername: host }, () => {
resolve(s.authorized);
s.destroy();
});
s.on("error", () => resolve(false));
});
}
if (!(await chainVerifies("internal.example.com"))) throw new Error("TLS chain invalid; run openssl s_client -showcerts"); Type guard
function isLeafSignatureError(e: unknown): e is Error & { code: "UNABLE_TO_VERIFY_LEAF_SIGNATURE" } {
return e instanceof Error && (e as any).code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
} Try / catch
try {
const res = await fetch(url);
} catch (e) {
if (isLeafSignatureError(e)) {
logger.warn("incomplete/mismatched chain on " + url, { hint: "serve full chain or pass tls.ca" });
return fallbackToKnownGoodEndpoint();
}
throw e;
} Prevention
- Monitor serving config so fullchain.pem (leaf+intermediate) is used, never cert.pem alone
- Run openssl s_client -showcerts in CI against your own endpoints
- Pass private intermediates explicitly via fetch(url, { tls: { ca } })
When it happens
Trigger: fetch()/Bun.connect with TLS to a server whose served chain is incomplete or misordered (leaf's real intermediate missing, so the verifier matches a different same-named issuer); issuer located but with a different public key; leaf signed with an algorithm BoringSSL will not verify.
Common situations: Server serves only the leaf cert (missing intermediate via certbot/nginx misconfig), chain file contains intermediates in reverse order, cert re-keyed by the CA but old intermediate still served, private PKI issuing from a re-keyed intermediate.
Related errors
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/40fc7c046f3fbc9b.
Report an issue: GitHub.