Hmbown/CodeWhale · error · anyhow::Error

Unable to compute next HOURLY run for BYDAY filter

Error message

Unable to compute next HOURLY run for BYDAY filter

What it means

Thrown by the unanchored HOURLY branch of AutomationSchedule::next_after_in_timezone when a BYDAY filter cannot be satisfied. The code advances a candidate time by INTERVAL hours for up to 24*21 = 504 steps and bails if candidate.weekday() never appears in the BYDAY list. Because each step advances by INTERVAL hours, an INTERVAL that is a multiple of 168 (a whole number of weeks, e.g. 168 or 336) freezes the weekday, so a BYDAY list excluding that weekday can never match.

Source

Thrown at crates/tui/src/automation_manager.rs:498

                            .ok_or_else(|| anyhow::anyhow!("HOURLY schedule exceeded its range"))?;
                    }
                    bail!("Unable to compute next anchored HOURLY run");
                }

                let after_second = local_after.second();
                let after_nanosecond = local_after.nanosecond();
                let mut candidate = local_after + Duration::hours(i64::from(*interval_hours))
                    - Duration::seconds(i64::from(after_second))
                    - Duration::nanoseconds(i64::from(after_nanosecond));

                if let Some(days) = byday {
                    for _ in 0..(24 * 21) {
                        if days.contains(&candidate.weekday()) {
                            return Ok(candidate.with_timezone(&Utc));
                        }
                        candidate += Duration::hours(i64::from(*interval_hours));
                    }
                    bail!("Unable to compute next HOURLY run for BYDAY filter");
                }

                Ok(candidate.with_timezone(&Utc))
            }
            Self::Weekly {
                byday,
                byhour,
                byminute,
            } => {
                for day_offset in 0..15 {
                    let date = local_after.date_naive() + Duration::days(i64::from(day_offset));
                    if !byday.contains(&date.weekday()) {
                        continue;
                    }
                    let Some(candidate_naive) = date.and_hms_opt(*byhour, *byminute, 0) else {
                        continue;
                    };
                    if let Some(candidate) = resolve_local_datetime(timezone, candidate_naive)

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Use FREQ=WEEKLY;BYDAY=MO;BYHOUR=H;BYMINUTE=M for once-per-week wall-clock schedules
  2. If HOURLY semantics are required, choose an INTERVAL that is not a multiple of 168 (e.g. 24), or include the frozen weekday in BYDAY
  3. Dry-run AutomationSchedule::parse_rrule plus a next-run computation before flipping an automation to Active
  4. Catch the error at the manager boundary and pause the automation instead of aborting the scheduling loop

Example fix

// before (weekday frozen, can never match)
rrule = "FREQ=HOURLY;INTERVAL=168;BYDAY=MO"

// after (weekly wall-clock schedule)
rrule = "FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;BYMINUTE=0"
Defensive patterns

Strategy: validation

Validate before calling

fn hourly_byday_reachable(rrule: &str) -> Result<bool, anyhow::Error> {
    if let AutomationSchedule::Hourly { interval_hours, byday: Some(days), .. } =
        AutomationSchedule::parse_rrule(rrule)?
    {
        if interval_hours > 0 && interval_hours % 168 == 0 {
            // Weekday is frozen at the first candidate's weekday.
            let first = chrono::Local::now()
                + chrono::Duration::hours(i64::from(interval_hours));
            return Ok(days.contains(&first.weekday()));
        }
    }
    Ok(true)
}

Try / catch

match manager.update_automation(id, req) {
    Ok(record) => { /* persisted with next_run_at */ }
    Err(e) if e.to_string().contains("HOURLY run for BYDAY filter") => {
        // Schedule can never fire: keep it paused and surface a config error.
        tracing::warn!(%e, "unreachable HOURLY/BYDAY automation {id}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Computing next_run_at for an Active automation (create_automation/update_automation with status Active) whose rrule is FREQ=HOURLY;INTERVAL=168;BYDAY=MO when the first candidate after 'now' is not a Monday: weekday never changes across 504 steps and next_after_with_anchor returns this error.

Common situations: Encoding a weekly intent as FREQ=HOURLY with INTERVAL=168 (assuming INTERVAL counts days); INTERVAL=24*N where N is a multiple of 7 combined with BYDAY; rrules copied from tools that accept multi-week hourly intervals.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/d8533accfa5f9766. Report an issue: GitHub.