tinyhumansai/openhuman · error
Invalid schedule: 'at' must be in the future
Error message
Invalid schedule: 'at' must be in the future
What it means
validate_schedule rejects a Schedule::At whose timestamp is <= the `now` it is validated against: a one-shot job scheduled in the past can never fire. Validation happens at job create/update, comparing against the caller-supplied current time.
Source
Thrown at src/openhuman/cron/schedule.rs:77
pub fn validate_schedule(schedule: &Schedule, now: DateTime<Utc>) -> Result<()> {
match schedule {
Schedule::Cron {
expr,
tz,
active_hours,
} => {
let _ = normalize_expression(expr)?;
if let Some(active) = active_hours {
let _ = ActiveWindow::parse(active)?;
}
let _ = ScheduleTimeZone::parse(tz.as_deref())?;
let _ = next_run_for_schedule(schedule, now)?;
Ok(())
}
Schedule::At { at } => {
if *at <= now {
anyhow::bail!("Invalid schedule: 'at' must be in the future");
}
Ok(())
}
Schedule::Every { every_ms } => {
if *every_ms == 0 {
anyhow::bail!("Invalid schedule: every_ms must be > 0");
}
Ok(())
}
}
}
pub fn schedule_cron_expression(schedule: &Schedule) -> Option<String> {
match schedule {
Schedule::Cron { expr, .. } => Some(expr.clone()),
_ => None,
}
}View on GitHub (pinned to 7491200858)
Solutions
- Compute the timestamp at submission — now + delay — serialized as UTC RFC3339 with a correct offset
- Add margin: require at > now + skew tolerance when latency between compose and submit is possible
- If the moment has genuinely passed, pick a new time or switch to a recurring Schedule::Cron / Schedule::Every
Example fix
// before: borderline, elapsed by validation time
Schedule::At { at: Utc::now() }
// after
Schedule::At { at: Utc::now() + ChronoDuration::minutes(5) } Defensive patterns
Strategy: validation
Validate before calling
const SKEW: chrono::Duration = chrono::Duration::seconds(30);
if let Schedule::At { at } = &schedule {
ensure!(*at > Utc::now() + SKEW, "'at' is in the past or too close to now");
} Prevention
- Generate 'at' server-side (now + user delay) rather than trusting client clocks
- Serialize timestamps with explicit UTC offsets
- Re-validate one-shots at submission time, not at draft-creation time
When it happens
Trigger: Creating a one-shot job with an already-elapsed timestamp; sending local time labelled as UTC (hours off, landing in the past); a delay between composing and submitting the job that outlives the chosen moment; client/host clock skew.
Common situations: RFC3339 strings built with the wrong offset; re-submitting an old job definition unchanged; paused drafts validated late; test fixtures with hardcoded past dates.
Related errors
- Cannot update expression/tz on a non-cron schedule
- No future occurrence found within active hours after 100,000
- Invalid schedule: every_ms must be > 0
- Not running in Tauri
- At least one of --expression, --tz, --command, or --name mus
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/0f848d2932e9f09e.
Report an issue: GitHub.