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
- Find the offending extension: openssl x509 -in cert.pem -noout -text and look for 'critical' next to nonstandard OIDs
- Have the cert reissued with that extension non-critical (or removed) — most CAs will do this on request
- If the extension is on an intermediate, switch to a chain variant without it (often an alternate cross-signed path exists)
- 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
- Never mark private-OID extensions critical in issued certs
- Lint issued certs for unknown critical extensions before deployment
- Avoid CT pre-certificates escaping into served chains
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.