oven-sh/bun · error · CertError

CERT_REJECTED

CERT_REJECTED

Error message

CERT_REJECTED

What it means

CertError::CERT_REJECTED maps BoringSSL X509 verify code 28 (X509_V_ERR_CERT_REJECTED) via get_cert_error_from_no() in src/http/lib.rs:1547. The root CA itself is trusted in the store but is not accepted for the requested purpose (TLS), so the chain is rejected at the anchor check. JS error.code is CERT_REJECTED with message 'certificate rejected' (FetchTasklet.rs:1463).

Source

Thrown at src/http/error.rs:177

    #[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")]
    UNHANDLED_CRITICAL_CRL_EXTENSION,
    #[error("INVALID_NON_CA")]

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Identify the anchor: openssl s_client -connect host:443 -showcerts — the last self-signed cert
  2. Replace the TLS chain with one rooted in a CA intended for SSL/TLS server auth
  3. If it is your own root, reissue the root with keyUsage=keyCertSign,cRLSign and no EKU restriction that excludes serverAuth
  4. Add the root explicitly per-connection via tls: { ca } so purpose rules come from the cert, not a restricted store entry

Example fix

# before: chain terminates in a code-signing root (rejected for TLS)
openssl verify -purpose sslserver -CAfile codesign-root.pem leaf.pem
# after: reissue under a TLS-capable root
openssl verify -purpose sslserver -CAfile tls-root.pem leaf.pem  # must print OK
Defensive patterns

Strategy: try-catch

Validate before calling

import { execSync } from "node:child_process";
const ok = execSync(`openssl verify -purpose sslserver -CAfile anchor.pem leaf.pem`).toString();
if (!ok.includes(": OK")) throw new Error("anchor rejected for TLS server purpose");

Type guard

function isCertRejected(e: unknown): e is Error & { code: "CERT_REJECTED" } {
  return e instanceof Error && (e as any).code === "CERT_REJECTED";
}

Try / catch

try { await fetch(url); } catch (e) { if (isCertRejected(e)) { escalateToSecurityTeam(`anchor not permitted for TLS: ${url}`); throw e; } throw e; }

Prevention

When it happens

Trigger: A root whose trust-store entry limits it to code signing or S/MIME is used to terminate a TLS server chain; purpose-specific trust (e.g., emailProtection-only root) during X509 purpose checking of an SSL connection.

Common situations: Repurposing a code-signing root for internal HTTPS, CA bundles installed for package signing (e.g., some artifact-signing roots) mistakenly serving TLS, exotic private CAs created without any TLS-compatible purpose.

Understand the failure class

Related errors


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