{"record":{"id":"533fab3423cfb973","repo":"oven-sh/bun","slug":"cert-has-expired","errorCode":null,"errorMessage":"CERT_HAS_EXPIRED","messagePattern":"CERT_HAS_EXPIRED","errorType":"error_code","errorClass":"CertError","httpStatus":null,"severity":"error","filePath":"src/http/error.rs","lineNumber":141,"sourceCode":"    #[error(\"OK\")]\n    OK,\n    #[error(\"UNABLE_TO_GET_ISSUER_CERT\")]\n    UNABLE_TO_GET_ISSUER_CERT,\n    #[error(\"UNABLE_TO_GET_CRL\")]\n    UNABLE_TO_GET_CRL,\n    #[error(\"UNABLE_TO_DECRYPT_CERT_SIGNATURE\")]\n    UNABLE_TO_DECRYPT_CERT_SIGNATURE,\n    #[error(\"UNABLE_TO_DECRYPT_CRL_SIGNATURE\")]\n    UNABLE_TO_DECRYPT_CRL_SIGNATURE,\n    #[error(\"UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY\")]\n    UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,\n    #[error(\"CERT_SIGNATURE_FAILURE\")]\n    CERT_SIGNATURE_FAILURE,\n    #[error(\"CRL_SIGNATURE_FAILURE\")]\n    CRL_SIGNATURE_FAILURE,\n    #[error(\"CERT_NOT_YET_VALID\")]\n    CERT_NOT_YET_VALID,\n    #[error(\"CERT_HAS_EXPIRED\")]\n    CERT_HAS_EXPIRED,\n    #[error(\"CRL_NOT_YET_VALID\")]\n    CRL_NOT_YET_VALID,\n    #[error(\"CRL_HAS_EXPIRED\")]\n    CRL_HAS_EXPIRED,\n    #[error(\"ERROR_IN_CERT_NOT_BEFORE_FIELD\")]\n    ERROR_IN_CERT_NOT_BEFORE_FIELD,\n    #[error(\"ERROR_IN_CERT_NOT_AFTER_FIELD\")]\n    ERROR_IN_CERT_NOT_AFTER_FIELD,\n    #[error(\"ERROR_IN_CRL_LAST_UPDATE_FIELD\")]\n    ERROR_IN_CRL_LAST_UPDATE_FIELD,\n    #[error(\"ERROR_IN_CRL_NEXT_UPDATE_FIELD\")]\n    ERROR_IN_CRL_NEXT_UPDATE_FIELD,\n    #[error(\"OUT_OF_MEM\")]\n    OUT_OF_MEM,\n    #[error(\"DEPTH_ZERO_SELF_SIGNED_CERT\")]\n    DEPTH_ZERO_SELF_SIGNED_CERT,\n    #[error(\"SELF_SIGNED_CERT_IN_CHAIN\")]","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/http/error.rs#L123-L159","documentation":"X509 verify result 10 (X509_V_ERR_CERT_HAS_EXPIRED): the certificate's notAfter is in the past, so it is no longer valid. Mapped via get_cert_error_from_no (src/http/lib.rs:1529) to CertError::CERT_HAS_EXPIRED, message \"certificate has expired\" (FetchTasklet.rs:1403). One of the most common TLS failures.","triggerScenarios":"Any TLS handshake (fetch, Bun.connect, proxied CONNECT tunnel) where the leaf or an intermediate in the served chain is past its expiry and reject_unauthorized is true (default).","commonSituations":"Forgotten Let's Encrypt renewals (90-day certs), expired certs on internal/staging servers nobody monitors, legacy appliances with 1-year certs, or browsers caching an old chain while the server already serves the renewed one.","solutions":["Renew the certificate on the server and reload the web server (certbot renew && systemctl reload nginx)","If already renewed, confirm the server actually serves the new chain: openssl s_client -connect host:443 | openssl x509 -noout -enddate","Set up expiry monitoring (certbot auto-renew timer, Prometheus blackbox_exporter) so it never recurs","For internal throwaway environments only: fetch(url, { tls: { rejectUnauthorized: false } }) as a stopgap"],"exampleFix":"# check\necho | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate\n# renew\ncertbot renew --force-renewal && systemctl reload nginx","handlingStrategy":"try-catch","validationCode":"// pre-check your OWN endpoint's expiry before it becomes an outage\nimport { connect as tlsConnect } from \"node:tls\";\nfunction daysUntilCertExpiry(host: string, port = 443): Promise<number> {\n  return new Promise((resolve, reject) => {\n    const s = tlsConnect({ host, port, servername: host, rejectUnauthorized: false }, () => {\n      const { valid_to } = s.getPeerCertificate();\n      s.end();\n      resolve((new Date(valid_to).getTime() - Date.now()) / 86_400_000);\n    });\n    s.once(\"error\", reject);\n  });\n}\nif (await daysUntilCertExpiry(\"api.example.com\") < 14) await alertOps(\"cert expiring\");","typeGuard":"function isCertErrorCode(e: unknown, code = \"CERT_HAS_EXPIRED\"): e is Error & { code: string } {\n  return e instanceof Error && (e as any).code === code;\n}","tryCatchPattern":"try {\n  await fetch(url);\n} catch (e) {\n  if (isCertErrorCode(e, \"CERT_HAS_EXPIRED\")) {\n    // server-side problem — page the owner; retrying won't help until renewal\n    throw new Error(\"Peer TLS certificate has expired\");\n  }\n  throw e;\n}","preventionTips":["Automate renewal (certbot timers / ACME clients) and alert at T-14 and T-3 days","Cert-expiry monitoring in blackbox checks for every public hostname","Reload the web server after renewal — a renewed file on disk doesn't update listening sockets"],"tags":["tls","certificates","expiry","fetch","network"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}