tinyhumansai/openhuman · error

No future occurrence found within active hours after 100,000

Error message

No future occurrence found within active hours after 100,000 attempts

What it means

For Schedule::Cron with active_hours, next_run_for_schedule walks candidate occurrences and returns the first inside the active window. If 100,000 consecutive candidates all fall outside the window it gives up: as configured, the job can never fire. validate_schedule calls this function, so the error typically surfaces at job create/update time.

Source

Thrown at src/openhuman/cron/schedule.rs:45

                    if active.contains(local_t) {
                        return Ok(next_utc);
                    }
                    tracing::debug!(
                        "[cron] next_run candidate {} outside active window {}–{}, advancing",
                        next_utc,
                        active.start,
                        active.end
                    );
                    current_from = next_utc;
                } else {
                    return Ok(next_utc);
                }
            }
            tracing::warn!(
                "[cron] no occurrence found within active_hours for expr={} after 100,000 candidates",
                expr
            );
            anyhow::bail!("No future occurrence found within active hours after 100,000 attempts")
        }
        Schedule::At { at } => Ok(*at),
        Schedule::Every { every_ms } => {
            if *every_ms == 0 {
                anyhow::bail!("Invalid schedule: every_ms must be > 0");
            }
            let ms = i64::try_from(*every_ms).context("every_ms is too large")?;
            let delta = ChronoDuration::milliseconds(ms);
            from.checked_add_signed(delta)
                .ok_or_else(|| anyhow::anyhow!("every_ms overflowed DateTime"))
        }
    }
}

pub fn validate_schedule(schedule: &Schedule, now: DateTime<Utc>) -> Result<()> {
    match schedule {
        Schedule::Cron {
            expr,

View on GitHub (pinned to 7491200858)

Solutions

  1. Widen active_hours so at least one expression fire time is inside it — or remove active_hours entirely
  2. Shift the expression into the window (e.g. '0 3 * * *' → '0 10 * * *' for a 09:00-17:00 window)
  3. Set the job's tz so expression and active_hours agree on the clock, then re-validate

Example fix

// before
Schedule::Cron { expr: "0 3 * * *".into(), tz: None, active_hours: Some("09:00-17:00".into()) }

// after
Schedule::Cron { expr: "0 10 * * *".into(), tz: None, active_hours: Some("09:00-17:00".into()) }
Defensive patterns

Strategy: validation

Validate before calling

// Authoring-time check: the job must have a reachable next run before persisting
validate_schedule(&schedule, Utc::now())?;
// or directly:
next_run_for_schedule(&schedule, Utc::now())?;

Try / catch

At job-create/update handlers, catch this bail distinctly and return 'schedule never fires within active_hours' so the UI can highlight the active-hours field rather than the expression (or vice versa).

Prevention

When it happens

Trigger: An expression that only fires outside the window — e.g. expr '0 3 * * *' (03:00) with active_hours '09:00-17:00'; timezone mismatch between the expression's tz and the window's clock; a narrowed window that no longer contains any fire time.

Common situations: Setting active_hours in local time while the expression is evaluated in the job tz; editing the window after the expression was chosen; DST shifts pushing the fire time out of the window; copy-pasted active_hours from a different job.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/6ba104a41cbed346. Report an issue: GitHub.