oven-sh/bun · error · CertError

SUBJECT_ISSUER_MISMATCH

SUBJECT_ISSUER_MISMATCH

Error message

SUBJECT_ISSUER_MISMATCH

What it means

CertError::SUBJECT_ISSUER_MISMATCH maps BoringSSL X509 verify code 29 (X509_V_ERR_SUBJECT_ISSUER_MISMATCH) via get_cert_error_from_no() in src/http/lib.rs:1548. The issuer DN on a certificate does not match the subject DN of the certificate that follows it in the presented chain, so the chain does not link up. JS error.code is SUBJECT_ISSUER_MISMATCH with message 'subject issuer mismatch' (FetchTasklet.rs:1469).

Source

Thrown at src/http/error.rs:179

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

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Extract the leaf's issuer DN: openssl x509 -in leaf.pem -noout -issuer and the next cert's subject: openssl x509 -in int.pem -noout -subject — they must be byte-identical
  2. Regenerate fullchain.pem from the current CA bundle shipped with the new cert (leaf + its actual intermediate, correct order)
  3. Restart/reload the server so the new chain is served
  4. Validate end-to-end: openssl s_client -connect host:443 -brief </dev/null

Example fix

# before
leaf.pem + old-intermediate.pem + old-root.pem
# after (after renewal, intermediate changed)
leaf.pem + R11-intermediate.pem
# verify linkage
openssl verify -show_chain -untrusted R11-intermediate.pem leaf.pem
Defensive patterns

Strategy: validation

Validate before calling

import { X509Certificate } from "node:crypto";
const leaf = new X509Certificate(await Bun.file("leaf.pem").text());
const int = new X509Certificate(await Bun.file("intermediate.pem").text());
if (leaf.issuer !== int.subject) {
  throw new Error(`chain broken: leaf issuer '${leaf.issuer}' != intermediate subject '${int.subject}'`);
}

Type guard

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

Try / catch

try { await fetch(url); } catch (e) { if (isSubjectIssuerMismatch(e)) { purgeHostCache(new URL(url).host); notifyCertOwner("served chain does not link up"); throw e; } throw e; }

Prevention

When it happens

Trigger: Server serves the wrong intermediate (one whose subject differs from the leaf's issuer field); chain PEMs concatenated out of order; certificate renewed under a different CA name but old intermediate still bundled.

Common situations: Certbot renewal switched intermediates (e.g., R10 -> R11) while nginx still serves the stale chain, manual fullchain assembly mixing cert generations, multi-CDN setups pasting mismatched bundles.

Related errors


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