tinyhumansai/openhuman · error

Invalid schedule: every_ms must be > 0

Error message

Invalid schedule: every_ms must be > 0

What it means

In next_run_for_schedule, Schedule::Every with every_ms == 0 is rejected: a zero interval would mean 'run continuously' and spin the scheduler. validate_schedule carries an identical guard (schedule.rs:83) so the same condition also fails at validation time.

Source

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

                        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,
            tz,
            active_hours,
        } => {
            let _ = normalize_expression(expr)?;
            if let Some(active) = active_hours {

View on GitHub (pinned to 7491200858)

Solutions

  1. Set a positive interval (every_ms > 0), sized to a sane floor (e.g. >= 1000ms) for scheduler load
  2. Clamp user-supplied frequencies at the boundary: every_ms = max(input, MIN_INTERVAL)
  3. If 'run immediately' was intended, that is not an interval — use a one-shot Schedule::At or trigger the job manually

Example fix

// before
Schedule::Every { every_ms: 0 }

// after
Schedule::Every { every_ms: 60_000 }
Defensive patterns

Strategy: validation

Validate before calling

if let Schedule::Every { every_ms } = &schedule {
    const MIN_INTERVAL_MS: u64 = 1_000;
    assert!(*every_ms >= MIN_INTERVAL_MS, "interval is zero / too small");
}

Type guard

function isValidEverySchedule(s: Schedule): boolean {
  return s.type !== 'every' || (s.every_ms ?? 0) > 0;
}

Prevention

When it happens

Trigger: Constructing an every-interval job with every_ms: 0 — UI frequency inputs defaulting to 0, division that computes 0, hand-written JSON, or deserialized legacy data where the field was never set — and then asking for its next run.

Common situations: Derived intervals (total/count landing on 0); 'run every N items' style inputs with N unset; test fixtures probing edge values.

Related errors


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