Hmbown/CodeWhale · warning

Unable to construct HOURLY anchor

Error message

Unable to construct HOURLY anchor

What it means

When a HOURLY schedule carries BYHOUR/BYMINUTE, next_run builds a wall-clock anchor with NaiveDate::and_hms_opt(hour, minute, 0), which returns None only if hour > 23 or minute > 59. The RRULE parser already rejects those ranges for HOURLY, so in practice this is a defensive invariant branch: it means an out-of-range anchor reached the scheduler without going through parse-time validation.

Source

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

                        "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)
                        .ok_or_else(|| anyhow::anyhow!("Unable to construct HOURLY anchor"))?;
                    let interval_seconds = i64::from(*interval_hours) * 60 * 60;
                    let elapsed_seconds = local_after
                        .naive_local()
                        .signed_duration_since(anchor_naive)
                        .num_seconds();
                    let mut steps = if elapsed_seconds < 0 {
                        0
                    } else {
                        elapsed_seconds / interval_seconds + 1
                    };

                    for _ in 0..MAX_HOURLY_SEARCH_STEPS {
                        let hours = i64::from(*interval_hours)
                            .checked_mul(steps)
                            .ok_or_else(|| anyhow::anyhow!("HOURLY schedule exceeded its range"))?;
                        let delta = Duration::try_hours(hours)
                            .ok_or_else(|| anyhow::anyhow!("HOURLY schedule exceeded its range"))?;
                        let candidate_naive = anchor_naive

View on GitHub (pinned to 8880682c63)

Solutions

  1. Keep BYHOUR in 0-23 and BYMINUTE in 0-59 in the RRULE
  2. Update or re-create the automation through the supported tooling so parse_rrule re-validates the stored record
  3. Audit the automation store for out-of-range anchor values if this error appears in logs

Example fix

// before (stored record that bypassed parse-time checks)
"FREQ=HOURLY;INTERVAL=24;BYHOUR=25;BYMINUTE=30"
// after
"FREQ=HOURLY;INTERVAL=24;BYHOUR=8;BYMINUTE=30"
Defensive patterns

Strategy: validation

Validate before calling

let (hour, minute) = (anchor_hour.unwrap_or(reference.hour()), anchor_minute.unwrap_or(0));
ensure!(hour <= 23 && minute <= 59, "anchor time {hour:02}:{minute:02} is not a valid wall clock");

Type guard

fn hourly_anchor_in_range(anchor_hour: Option<u32>, anchor_minute: Option<u32>) -> bool {
    anchor_hour.is_none_or(|h| h <= 23) && anchor_minute.is_none_or(|m| m <= 59)
}

Prevention

When it happens

Trigger: A stored automation record deserialized from an older version or hand-edited on disk with BYHOUR > 23 or BYMINUTE > 59, skipping parse_rrule's range checks; or future code that constructs AutomationSchedule::Hourly directly without validating.

Common situations: Automations persisted before the current validation existed; manual edits to the automation store; downgraded binaries scheduling records written by a newer schema.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/6a1ce4055a42df86. Report an issue: GitHub.