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
- 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
- Regenerate fullchain.pem from the current CA bundle shipped with the new cert (leaf + its actual intermediate, correct order)
- Restart/reload the server so the new chain is served
- 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
- Regenerate fullchain.pem automatically on cert renewal (never hand-edit)
- Assert leaf.issuer === intermediate.subject in deploy scripts using node:crypto X509Certificate
- Poll your own endpoints with openssl s_client after deploys
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.