oven-sh/bun · error · TypeError

UNHANDLED_CRITICAL_EXTENSION

UNHANDLED_CRITICAL_EXTENSION

Error message

UNHANDLED_CRITICAL_EXTENSION

What it means

CertError::UNHANDLED_CRITICAL_EXTENSION maps BoringSSL X509 verify code 34 (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) via get_cert_error_from_no() in src/http/lib.rs:1553. The certificate contains an extension marked critical that the verifier does not recognize or process; per RFC 5280 the chain must then be rejected. JS error.code is UNHANDLED_CRITICAL_EXTENSION with message 'unhandled critical extension' (FetchTasklet.rs:1484).

Source

Thrown at src/http/error.rs:189

    #[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")]
    INVALID_NON_CA,
    #[error("PROXY_PATH_LENGTH_EXCEEDED")]
    PROXY_PATH_LENGTH_EXCEEDED,
    #[error("KEYUSAGE_NO_DIGITAL_SIGNATURE")]
    KEYUSAGE_NO_DIGITAL_SIGNATURE,
    #[error("PROXY_CERTIFICATES_NOT_ALLOWED")]
    PROXY_CERTIFICATES_NOT_ALLOWED,
    #[error("INVALID_EXTENSION")]
    INVALID_EXTENSION,
    #[error("INVALID_POLICY_EXTENSION")]
    INVALID_POLICY_EXTENSION,
    #[error("NO_EXPLICIT_POLICY")]

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Find the offending extension: openssl x509 -in cert.pem -noout -text and look for 'critical' next to nonstandard OIDs
  2. Have the cert reissued with that extension non-critical (or removed) — most CAs will do this on request
  3. If the extension is on an intermediate, switch to a chain variant without it (often an alternate cross-signed path exists)
  4. Do not disable verification globally; if forced for an internal endpoint, scope it: tls: { rejectUnauthorized: false } on that one request, test only

Example fix

# before
[ext]
1.3.6.1.4.1.99999.1=critical,DER:...   # private OID marked critical
# after
[ext]
1.3.6.1.4.1.99999.1=DER:...            # drop critical flag
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -extfile ext.cnf -out server.pem
Defensive patterns

Strategy: try-catch

Validate before calling

import { X509Certificate } from "node:crypto";
const cert = new X509Certificate(await Bun.file("server.pem").text());
const text = await Bun.$`openssl x509 -in server.pem -noout -text`.text();
const unknownCritical = [...text.matchAll(/(\d+\.\d+(?:\.\d+)+):\s*critical/g)]
  .map((m) => m[1])
  .filter((oid) => !oid.startsWith("2.5.29.")); // outside standard ext arc
if (unknownCritical.length) throw new Error("nonstandard critical extension(s): " + unknownCritical.join(", "));

Type guard

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

Try / catch

try { await fetch(url); } catch (e) { if (isUnhandledCriticalExt(e)) { openCertTicket(new URL(url).host, "nonstandard critical extension"); throw e; } throw e; }

Prevention

When it happens

Trigger: Server certificate (or an intermediate) carries a private-OID or uncommon extension flagged critical — e.g., Certificate Transparency poison, vendor-specific policy OIDs, legacy Microsoft templates — none of which BoringSSL handles during fetch()'s verification.

Common situations: Certs issued by unusual/legacy CAs or enterprise Microsoft ADCS templates that mark proprietary OIDs critical, pre-certificate (CT poison) artifacts escaping into production chains, certs produced by custom openssl configs with addext and critical set.

Related errors


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