oven-sh/bun · error · CertError
DEPTH_ZERO_SELF_SIGNED_CERT
DEPTH_ZERO_SELF_SIGNED_CERT
Error message
DEPTH_ZERO_SELF_SIGNED_CERT
What it means
X509 verify result 18 (X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT): the leaf certificate itself is self-signed (issuer == subject) and is not present in the trust store, so it cannot be trusted. Mapped via get_cert_error_from_no (src/http/lib.rs:1537) to CertError::DEPTH_ZERO_SELF_SIGNED_CERT (message per FetchTasklet.rs table).
Source
Thrown at src/http/error.rs:157
#[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")]
ERROR_IN_CRL_LAST_UPDATE_FIELD,
#[error("ERROR_IN_CRL_NEXT_UPDATE_FIELD")]
ERROR_IN_CRL_NEXT_UPDATE_FIELD,
#[error("OUT_OF_MEM")]
OUT_OF_MEM,
#[error("DEPTH_ZERO_SELF_SIGNED_CERT")]
DEPTH_ZERO_SELF_SIGNED_CERT,
#[error("SELF_SIGNED_CERT_IN_CHAIN")]
SELF_SIGNED_CERT_IN_CHAIN,
#[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")]View on GitHub (pinned to 8c5296ac45)
Solutions
- For local dev, use mkcert to create a locally-trusted cert: mkcert -install && mkcert localhost
- Or trust the exact self-signed cert on the client: fetch(url, { tls: { ca: selfSignedPem } }) / NODE_EXTRA_CA_CERTS
- Get a real certificate (Let's Encrypt / internal CA-issued) for anything non-local
- Temporary stopgap only for localhost: fetch(url, { tls: { rejectUnauthorized: false } }) — scope it to localhost so it can't be used against public hosts
Example fix
# before
await fetch("https://localhost:3000/api"); // DEPTH_ZERO_SELF_SIGNED_CERT
# after (dev): mkcert -install && mkcert localhost
# serve localhost.pem, and no client change is needed Defensive patterns
Strategy: try-catch
Validate before calling
// for local dev: detect a self-signed endpoint up front and scope any bypass to localhost
const isLocal = (u: string) => /^(https?:\/\/)?(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/.test(u);
if (!isLocal(target) && process.env.ALLOW_INSECURE_TLS === "1") {
throw new Error("Insecure TLS bypass is only permitted for localhost targets");
} Type guard
function isCertErrorCode(e: unknown, code = "DEPTH_ZERO_SELF_SIGNED_CERT"): e is Error & { code: string } {
return e instanceof Error && (e as any).code === code;
} Try / catch
try {
await fetch("https://localhost:3000/api");
} catch (e) {
if (isCertErrorCode(e, "DEPTH_ZERO_SELF_SIGNED_CERT")) {
// dev-only escape hatch, scoped to localhost
const res = await fetch("https://localhost:3000/api", { tls: { rejectUnauthorized: false } });
} else throw e;
} Prevention
- Use mkcert for local TLS so dev servers get locally-trusted certs and no bypass is needed
- Never commit rejectUnauthorized: false for non-localhost hosts — gate it behind an env var plus a localhost regex
- Provision real certificates for anything reachable outside the dev machine
When it happens
Trigger: fetch("https://localhost:...") or Bun.connect against a local dev server using `openssl req -x509` self-signed certs, an IoT/appliance web UI, or any endpoint presenting a self-signed leaf — with default verification on.
Common situations: Local development (next.js/vite dev servers with self-signed TLS), internal tools with never-provisioned certs, hardware devices, k8s services with self-generated certs.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- UNABLE_TO_GET_ISSUER_CERT
- UNABLE_TO_GET_CRL
- UNABLE_TO_DECRYPT_CERT_SIGNATURE
- UNABLE_TO_DECRYPT_CRL_SIGNATURE
- UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/573164530a5b7b6c.
Report an issue: GitHub.