BloopAI/vibe-kanban · error

date overflow for digest schedule

Error message

date overflow for digest schedule

What it means

`checked_add_days` returns None when adding the days would overflow the date range (NaiveDate max is ~year 262143). The code expects this never happens, panicking with "date overflow for digest schedule" otherwise. It is a guard against scheduling arithmetic at the edge of chrono's representable date range.

Source

Thrown at crates/remote/src/digest/task.rs:151

        Err(error) => {
            error!(error = %error, "Failed to acquire notification digest lock");
            None
        }
    }
}

fn next_run_at(now: DateTime<Utc>, run_hour_utc: u32) -> DateTime<Utc> {
    let today = now.date_naive();
    let today_run = today
        .and_hms_opt(run_hour_utc, 0, 0)
        .expect("validated digest hour");

    let next_naive = if now.hour() < run_hour_utc {
        today_run
    } else {
        today
            .checked_add_days(Days::new(1))
            .expect("date overflow for digest schedule")
            .and_hms_opt(run_hour_utc, 0, 0)
            .expect("validated digest hour")
    };

    DateTime::from_naive_utc_and_offset(next_naive, Utc)
}

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Treat as effectively impossible but fail gracefully: replace expect with unwrap_or_else(|| now + chrono::Duration::hours(24))
  2. Use checked arithmetic end-to-end and propagate a Result to digest_loop so a bad clock doesn't crash the process
  3. Log and reset the schedule if system time looks implausible (e.g. year > 9999)

Example fix

// before
.checked_add_days(Days::new(1)).expect("date overflow for digest schedule")
// after
.checked_add_days(Days::new(1))
    .unwrap_or_else(|| now.date_naive().succ_opt().unwrap_or(now.date_naive()))
Defensive patterns

Strategy: fallback

Validate before calling

fn plausible_now(now: DateTime<Utc>) -> bool { now.year() >= 1970 && now.year() < 9999 }

Try / catch

match std::panic::catch_unwind(|| next_run_at(now, hour)) { Ok(t) => t, Err(_) => now + chrono::Duration::hours(24) }

Prevention

When it happens

Trigger: `digest_loop` calls `next_run_at` with a `now` (Utc::now()) whose date is near NaiveDate::MAX so adding 1 day overflows and checked_add_days returns None.

Common situations: Practically unreachable in normal operation; could appear with a wildly incorrect system clock (year far beyond 262000), clock skew/failure producing sentinel dates, or tests passing synthetic far-future timestamps.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/12a00d1f2ccd87a4. Report an issue: GitHub.