oven-sh/bun · error · TypeError
INVALID_NON_CA
INVALID_NON_CA
Error message
INVALID_NON_CA
What it means
CertError::INVALID_NON_CA maps BoringSSL X509 verify code 37 (X509_V_ERR_INVALID_NON_CA) via get_cert_error_from_no() in src/http/lib.rs:1556. A certificate in the middle of the chain (not the trust anchor, not self-issued) carries CA:TRUE Basic Constraints when it must not act as a CA, so the chain is rejected as malformed. JS error.code is INVALID_NON_CA with message 'invalid non-CA certificate (has CA markings)' (FetchTasklet.rs:1443).
Source
Thrown at src/http/error.rs:195
#[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")]
NO_EXPLICIT_POLICY,
#[error("DIFFERENT_CRL_SCOPE")]
DIFFERENT_CRL_SCOPE,
#[error("UNSUPPORTED_EXTENSION_FEATURE")]
UNSUPPORTED_EXTENSION_FEATURE,
#[error("UNNESTED_RESOURCE")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Find which chain element has CA:TRUE but is not the anchor: openssl x509 -in cert.pem -noout -text | grep -A2 'Basic Constraints' for each cert in order
- Reissue that certificate with basicConstraints=CA:FALSE (server leafs normally omit or set CA:FALSE)
- Rebuild fullchain.pem with only properly-issued certs and reload the server
- Re-test: openssl s_client -connect host:443 -brief </dev/null
Example fix
# before: leaf mistakenly issued with CA:TRUE [ext] basicConstraints=critical,CA:TRUE,pathlen:1 # after [ext] basicConstraints=critical,CA:FALSE keyUsage=critical,digitalSignature,keyEncipherment extendedKeyUsage=serverAuth
Defensive patterns
Strategy: validation
Validate before calling
import { X509Certificate } from "node:crypto";
for (const f of ["leaf.pem", "intermediate.pem"]) {
const cert = new X509Certificate(await Bun.file(f).text());
if (f === "leaf.pem" && cert.ca) {
throw new Error("leaf must not carry CA:TRUE — INVALID_NON_CA will fail verification");
}
} Type guard
function isInvalidNonCa(e: unknown): e is Error & { code: "INVALID_NON_CA" } {
return e instanceof Error && (e as any).code === "INVALID_NON_CA";
} Try / catch
try { await fetch(url); } catch (e) { if (isInvalidNonCa(e)) { notifyCertOwner(new URL(url).host, "CA markings on non-CA cert"); throw e; } throw e; } Prevention
- Separate CA and server templates; never clone CA profiles for leafs
- Assert leaf X509Certificate#ca is false in issuance lint
- Audit chains with openssl x509 -text for stray CA:TRUE flags
When it happens
Trigger: Leaf/intermediate certificate erroneously issued with basicConstraints=CA:TRUE (or CA markup) appears as a non-terminal link in the presented chain; commonly a server cert template that inherited CA flags.
Common situations: Misissued internal certs where the template copied CA extensions, chains hand-assembled with an extra CA-marked leaf appended, CA tooling bugs marking intermediates from non-CA profiles.
Related errors
- UNABLE_TO_VERIFY_LEAF_SIGNATURE
- CERT_CHAIN_TOO_LONG
- PATH_LENGTH_EXCEEDED
- SUBJECT_ISSUER_MISMATCH
- AKID_SKID_MISMATCH
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/58035e6e6a522b70.
Report an issue: GitHub.