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

  1. 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
  2. Reissue that certificate with basicConstraints=CA:FALSE (server leafs normally omit or set CA:FALSE)
  3. Rebuild fullchain.pem with only properly-issued certs and reload the server
  4. 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

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


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