oven-sh/bun · error · CertError

UNABLE_TO_GET_CRL

Error message

UNABLE_TO_GET_CRL

What it means

X509 verify result 3 (X509_V_ERR_UNABLE_TO_GET_CRL): certificate-chain validation was configured to check revocation via CRLs, and no Certificate Revocation List could be found for a certificate in the chain. Bun translates the BoringSSL verify code through get_cert_error_from_no (src/http/lib.rs:1522) into CertError::UNABLE_TO_GET_CRL, rejecting the TLS handshake with message "unable to get certificate CRL" (FetchTasklet.rs:1379).

Source

Thrown at src/http/error.rs:127

    Sys(#[from] bun_errno::SystemErrno),
    #[error(transparent)]
    Zlib(bun_zlib::ZlibError),
    #[error(transparent)]
    Brotli(bun_brotli::Error),
    #[error(transparent)]
    Zstd(bun_zstd::ZstdError),
    #[error(transparent)]
    Picohttp(bun_picohttp::ParseResponseError),
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error, strum::IntoStaticStr)]
pub enum CertError {
    #[error("OK")]
    OK,
    #[error("UNABLE_TO_GET_ISSUER_CERT")]
    UNABLE_TO_GET_ISSUER_CERT,
    #[error("UNABLE_TO_GET_CRL")]
    UNABLE_TO_GET_CRL,
    #[error("UNABLE_TO_DECRYPT_CERT_SIGNATURE")]
    UNABLE_TO_DECRYPT_CERT_SIGNATURE,
    #[error("UNABLE_TO_DECRYPT_CRL_SIGNATURE")]
    UNABLE_TO_DECRYPT_CRL_SIGNATURE,
    #[error("UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY")]
    UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
    #[error("CERT_SIGNATURE_FAILURE")]
    CERT_SIGNATURE_FAILURE,
    #[error("CRL_SIGNATURE_FAILURE")]
    CRL_SIGNATURE_FAILURE,
    #[error("CERT_NOT_YET_VALID")]
    CERT_NOT_YET_VALID,
    #[error("CERT_HAS_EXPIRED")]
    CERT_HAS_EXPIRED,
    #[error("CRL_NOT_YET_VALID")]
    CRL_NOT_YET_VALID,
    #[error("CRL_HAS_EXPIRED")]

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Publish and host the CA's CRL at the cRLDistributionPoint URL embedded in the certificate and make sure it is reachable
  2. Disable mandatory CRL checking on the client for that host (rely on OCSP or the CA's assurance) — Bun's default fetch does not require CRLs, so remove any custom checkServerIdentity/verify setup that enables it
  3. Switch the PKI to OCSP-based revocation so no CRL lookup is needed
  4. For internal CAs where revocation is handled operationally, add the CA to the trust list so leaf checks short-circuit before CRL lookup
Defensive patterns

Strategy: try-catch

Type guard

function isCertErrorCode(e: unknown, code = "UNABLE_TO_GET_CRL"): e is Error & { code: string } {
  return e instanceof Error && (e as any).code === code;
}

Try / catch

try {
  await fetch(url);
} catch (e) {
  if (isCertErrorCode(e, "UNABLE_TO_GET_CRL")) {
    // CRL infrastructure problem — escalate to the PKI team rather than bypassing
    throw new Error("Revocation (CRL) unavailable for peer certificate");
  }
  throw e;
}

Prevention

When it happens

Trigger: A TLS connection (fetch/https/Bun.connect, including proxied CONNECT tunnels) where CRL-based revocation checking is active and the CA publishes no (or an unreachable) CRL for the cert. Rare with Bun's default configuration, which does not require CRLs.

Common situations: Internal PKIs with CRL checking enabled but no CRL distribution points hosted, a CRL URL that is down/unreachable (the verifier treats it as missing), or hardened environments that turn on strict revocation checking.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/6db38542918c32f3. Report an issue: GitHub.