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_naiveView on GitHub (pinned to 8880682c63)
Solutions
- Keep BYHOUR in 0-23 and BYMINUTE in 0-59 in the RRULE
- Update or re-create the automation through the supported tooling so parse_rrule re-validates the stored record
- 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
- Never bypass parse_rrule when writing automation records — it enforces BYHOUR 0-23 and BYMINUTE 0-59
- Re-validate stored records after upgrades that changed the RRULE grammar
- Do not hand-edit the automation store; use update actions so validation re-runs
- Treat this error as an invariant tripwire: its appearance means stale data, not user error
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
- HOURLY schedule exceeded its range
- WEEKLY schedules require BYDAY
- WEEKLY schedules require BYHOUR
- WEEKLY schedules require BYMINUTE
- Unable to round CRON search start
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/6a1ce4055a42df86.
Report an issue: GitHub.