Hmbown/CodeWhale · warning · anyhow::Error
Once schedule has no future run after {}
Error message
Once schedule has no future run after {} What it means
next_after_in_timezone for a Once { at } schedule can only ever return the single stored instant. When asked for the next run strictly after a moment that is at or past `at`, there is nothing left to fire, so it bails with the cutoff timestamp. This is the natural end-of-life of a one-shot schedule, surfaced as an error rather than a silent skip.
Source
Thrown at crates/tui/src/automation_manager.rs:425
after: DateTime<Utc>,
anchor_reference: DateTime<Utc>,
) -> Result<DateTime<Utc>> {
self.next_after_in_timezone(after, anchor_reference, &Local)
}
fn next_after_in_timezone<Tz: TimeZone>(
&self,
after: DateTime<Utc>,
anchor_reference: DateTime<Utc>,
timezone: &Tz,
) -> Result<DateTime<Utc>> {
let local_after = after.with_timezone(timezone);
match self {
Self::Once { at } => {
if *at > after {
Ok(*at)
} else {
bail!(
"Once schedule has no future run after {}",
after.to_rfc3339()
)
}
}
Self::Hourly {
interval_hours,
byday,
anchor_hour,
anchor_minute,
} => {
if anchor_hour.is_some() || anchor_minute.is_some() {
let local_anchor_reference = anchor_reference.with_timezone(timezone);
let hour = anchor_hour.unwrap_or(local_anchor_reference.hour());
let minute = anchor_minute.unwrap_or(0);
let anchor_naive = local_anchor_reference
.date_naive()
.and_hms_opt(hour, minute, 0)View on GitHub (pinned to 0c42157ee5)
Solutions
- Treat this error as schedule completion: mark the automation done/disable it instead of retrying.
- For missed one-shots you still want, recreate the rule with a future AT (`FREQ=ONCE;AT=...`) or switch to CRON.
- When previewing, check `if let AutomationSchedule::Once { at } = sched { if *at <= after { /* expired */ } }` before calling.
Example fix
// before
let next = sched.next_after(after, &tz)?; // bails: Once schedule has no future run
// after
let next = match sched.next_after(after, &tz) {
Ok(t) => t,
Err(e) if e.to_string().contains("no future run") => {
mark_automation_complete(&id); // one-shot fired/expired
return Ok(());
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: fallback
Validate before calling
if let AutomationSchedule::Once { at } = &schedule {
if *at <= after {
// One-shot already fired or expired; nothing to schedule.
disable_automation(&id)?;
return Ok(None);
}
}
let next = schedule.next_after(after, &tz)?; Type guard
fn is_expired_once(sched: &AutomationSchedule, after: chrono::DateTime<chrono::Utc>) -> bool {
matches!(sched, AutomationSchedule::Once { at } if *at <= after)
} Try / catch
match schedule.next_after(after, &tz) {
Ok(next) => Some(next),
Err(e) if e.to_string().contains("no future run") => { mark_automation_complete(&id); None }
Err(e) => return Err(e),
} Prevention
- Treat expired ONCE schedules as completion, not as an error to retry.
- Disable or clean up one-shot automations after they fire.
- Use CRON or recurring FREQ variants when late/missed firing matters.
When it happens
Trigger: Calling next_after/next_after_in_timezone on an ONCE schedule after its AT time has passed — e.g. the scheduler wakes late (machine asleep, process suspended) and recomputes the next run, or a missed-run recovery path asks what comes next.
Common situations: Laptop asleep at fire time; long queue or downtime; tests iterating next-run with an `after` beyond AT; UI previewing the 'next occurrence' of an expired one-shot.
Related errors
- Invalid RRULE segment '{item}'
- Unsupported RRULE FREQ '{other}'. Supported: ONCE, HOURLY, W
- RRULE must include FREQ
- Unsupported RRULE field '{key}' for HOURLY. Allowed: FREQ,IN
- INTERVAL must be >= 1 for HOURLY schedules
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/3ce1f8f3578f4f47.
Report an issue: GitHub.