oven-sh/bun · error · CertError
UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
Error message
UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
What it means
X509 verify result 6 (X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY): the issuer certificate's public key could not be decoded (bad SubjectPublicKeyInfo), so signatures cannot be checked. Mapped via get_cert_error_from_no (src/http/lib.rs:1525) to CertError::UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, message "unable to decode issuer public key" (FetchTasklet.rs:1388).
Source
Thrown at src/http/error.rs:133
Zstd(bun_zstd::ZstdError),
#[error(transparent)]
Picohttp(bun_picohttp::ParseResponseError),
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error, strum::IntoStaticStr)]
pub enum CertError {
#[error("OK")]
OK,
#[error("UNABLE_TO_GET_ISSUER_CERT")]
UNABLE_TO_GET_ISSUER_CERT,
#[error("UNABLE_TO_GET_CRL")]
UNABLE_TO_GET_CRL,
#[error("UNABLE_TO_DECRYPT_CERT_SIGNATURE")]
UNABLE_TO_DECRYPT_CERT_SIGNATURE,
#[error("UNABLE_TO_DECRYPT_CRL_SIGNATURE")]
UNABLE_TO_DECRYPT_CRL_SIGNATURE,
#[error("UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY")]
UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
#[error("CERT_SIGNATURE_FAILURE")]
CERT_SIGNATURE_FAILURE,
#[error("CRL_SIGNATURE_FAILURE")]
CRL_SIGNATURE_FAILURE,
#[error("CERT_NOT_YET_VALID")]
CERT_NOT_YET_VALID,
#[error("CERT_HAS_EXPIRED")]
CERT_HAS_EXPIRED,
#[error("CRL_NOT_YET_VALID")]
CRL_NOT_YET_VALID,
#[error("CRL_HAS_EXPIRED")]
CRL_HAS_EXPIRED,
#[error("ERROR_IN_CERT_NOT_BEFORE_FIELD")]
ERROR_IN_CERT_NOT_BEFORE_FIELD,
#[error("ERROR_IN_CERT_NOT_AFTER_FIELD")]
ERROR_IN_CERT_NOT_AFTER_FIELD,
#[error("ERROR_IN_CRL_LAST_UPDATE_FIELD")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Re-download the CA/intermediate from the authoritative source and validate: openssl x509 -in ca.pem -noout -text
- Check the supplied tls.ca / NODE_EXTRA_CA_CERTS file: every block must be a complete -----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----
- Convert DER to PEM properly if needed: openssl x509 -inform DER -in ca.der -out ca.pem
- Remove the corrupt entry and retry to isolate which file is broken
Example fix
// before: accidental key file passed as CA
await fetch(url, { tls: { ca: readFileSync("ca.key", "utf8") } });
// after
await fetch(url, { tls: { ca: readFileSync("ca.pem", "utf8") } }); Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from "node:fs";
function loadCaBundle(path: string): string {
const pem = readFileSync(path, "utf8");
const blocks = pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) ?? [];
if (blocks.length === 0) throw new Error(`CA bundle '${path}' contains no valid PEM certificates`);
return blocks.join("\n");
}
const ca = loadCaBundle("./ca.pem"); // fails fast before any fetch
await fetch(url, { tls: { ca } }); Type guard
function isCertErrorCode(e: unknown, code = "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"): e is Error & { code: string } {
return e instanceof Error && (e as any).code === code;
} Try / catch
try {
await fetch(url, { tls: { ca } });
} catch (e) {
if (isCertErrorCode(e, "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY")) {
// the CA/intermediate you supplied is malformed — fix the bundle, don't retry
throw new Error("Provided CA bundle contains a malformed certificate");
}
throw e;
} Prevention
- Validate PEM bundles at startup with a BEGIN/END block parser
- Fetch CA files from authoritative sources; never hand-edit
- Use openssl x509 -noout -text in CI to prove every bundle member parses
When it happens
Trigger: TLS handshake where the intermediate/CA certificate presented (or supplied via tls.ca / NODE_EXTRA_CA_CERTS) is malformed — truncated PEM, wrong file contents, or a DER/PEM mixup.
Common situations: A truncated or hand-edited CA bundle, pasting an HTML page instead of the PEM, appending binary DER data into a .pem file, or supplying a chain file where a key file was expected.
Related errors
- UNABLE_TO_GET_ISSUER_CERT
- UNABLE_TO_DECRYPT_CERT_SIGNATURE
- CERT_SIGNATURE_FAILURE
- ERROR_IN_CERT_NOT_BEFORE_FIELD
- ERROR_IN_CERT_NOT_AFTER_FIELD
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/041dd66bbf4be1b0.
Report an issue: GitHub.