oven-sh/bun · error · CertError
CERT_NOT_YET_VALID
Error message
CERT_NOT_YET_VALID
What it means
X509 verify result 9 (X509_V_ERR_CERT_NOT_YET_VALID): the certificate's notBefore (validity start) is in the future relative to the local clock, so the cert is not yet valid. Mapped via get_cert_error_from_no (src/http/lib.rs:1528) to CertError::CERT_NOT_YET_VALID, message "certificate is not yet valid" (FetchTasklet.rs:1397).
Source
Thrown at src/http/error.rs:139
#[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")]
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")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Sync the system clock (timedatectl set-ntp true / restart chrony or ntpd; restart the container/VM if it inherited a bad clock)
- Confirm the skew: date -u && curl -sI https://example.com | grep -i date
- If the cert genuinely starts in the future (CA clock skew), wait until the notBefore time passes and retry
- For lab environments only, bypass with fetch(url, { tls: { rejectUnauthorized: false } }) while the clock is fixed
Example fix
# diagnose skew date -u sudo timedatectl set-ntp true # fix clock, then retry
Defensive patterns
Strategy: validation
Validate before calling
function clockIsPlausible(maxSkewMs = 5 * 60_000): boolean {
return Math.abs(Date.now() - expectedWallClockMs()) <= maxSkewMs;
}
// cheap check: compare against a trusted local source, e.g. an NTP-adjusted time service,
// before firing TLS-heavy batch jobs
if (!clockIsPlausible()) throw new Error("System clock skewed — fix NTP before TLS calls"); Type guard
function isCertErrorCode(e: unknown, code = "CERT_NOT_YET_VALID"): e is Error & { code: string } {
return e instanceof Error && (e as any).code === code;
} Try / catch
try {
await fetch(url);
} catch (e) {
if (isCertErrorCode(e, "CERT_NOT_YET_VALID")) {
// usually local clock skew: alert ops to fix NTP instead of disabling verification
throw new Error("Peer certificate not yet valid — check system clock/NTP");
}
throw e;
} Prevention
- Enable NTP/chrony on all hosts and containers; verify with timedatectl in provisioning
- Run date -u sanity checks at job startup for long-lived daemons on VMs that suspend
- Don't disable verification to 'fix' clock problems — fix the clock
When it happens
Trigger: fetch/https/Bun.connect TLS handshake where the peer cert (or an intermediate) has notBefore > current system time — usually wrong local clock rather than a bad cert.
Common situations: Containers/VMs booted with a stale or skewed clock (no NTP), Raspberry Pi without RTC battery, laptops resumed from suspend with drifted time, or certs issued seconds ago against a clock a few minutes behind.
Related errors
- CRL_NOT_YET_VALID
- UNABLE_TO_GET_ISSUER_CERT
- UNABLE_TO_GET_CRL
- UNABLE_TO_DECRYPT_CERT_SIGNATURE
- UNABLE_TO_DECRYPT_CRL_SIGNATURE
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/32371f1fa1c34e32.
Report an issue: GitHub.