HKUDS/DeepTutor · error · ValueError

'at' time is in the past

Error message

'at' time is in the past

What it means

validate_schedule rejects one-shot 'at' schedules whose at_ms is <= the current epoch milliseconds. A past-dated job can never fire, so it is rejected up front using the server's clock (_now_ms()).

Source

Thrown at deeptutor/services/cron/service.py:157

            return int(next_dt.timestamp() * 1000)
        except ImportError:
            raise ValueError(
                "cron expressions need the 'croniter' package — "
                "use an 'every' or 'at' schedule instead"
            ) from None
        except Exception as exc:
            raise ValueError(f"invalid cron expression {schedule.expr!r}: {exc}") from None

    return None


def validate_schedule(schedule: CronSchedule) -> None:
    """Reject schedules that could never run (raises ValueError)."""
    if schedule.kind == "at":
        if not schedule.at_ms:
            raise ValueError("'at' schedules need a time")
        if schedule.at_ms <= _now_ms():
            raise ValueError("'at' time is in the past")
        return
    if schedule.kind == "every":
        if not schedule.every_seconds or schedule.every_seconds < 30:
            raise ValueError("'every' interval must be at least 30 seconds")
        return
    if schedule.kind == "cron":
        if schedule.tz:
            try:
                from zoneinfo import ZoneInfo

                ZoneInfo(schedule.tz)
            except Exception:
                raise ValueError(f"unknown timezone {schedule.tz!r}") from None
        # Raises ValueError on bad/unsupported expressions.
        if compute_next_run(schedule, _now_ms()) is None:
            raise ValueError(f"cron expression {schedule.expr!r} never fires")
        return
    raise ValueError(f"unknown schedule kind {schedule.kind!r}")

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Recompute at_ms from server time right before calling add_job (now + delta)
  2. Add a buffer (e.g. +60s) to user-chosen times to absorb latency and clock skew
  3. Sync system clocks (NTP) on hosts that generate at_ms

Example fix

# before
at_ms = user_selected_epoch_ms  # may already be past
svc.add_job(CronSchedule(kind="at", at_ms=at_ms), ...)
# after
import time
now = int(time.time() * 1000)
at_ms = max(user_selected_epoch_ms, now + 60_000)
svc.add_job(CronSchedule(kind="at", at_ms=at_ms), ...)
Defensive patterns

Strategy: validation

Validate before calling

import time
def at_in_future(s: CronSchedule, skew_ms: int = 60_000) -> bool:
    return bool(s.at_ms) and s.at_ms > int(time.time() * 1000) + skew_ms

Try / catch

try:
    svc.add_job(schedule, ...)
except ValueError as e:
    if "in the past" in str(e):
        schedule.at_ms = int((time.time() + 3600) * 1000)  # reschedule +1h
        svc.add_job(schedule, ...)
    else:
        raise

Prevention

When it happens

Trigger: Passing at_ms in the past (or exactly now) to add_job; a delay between computing at_ms and submission letting the target time pass; server clock skew.

Common situations: Client computes the timestamp with an out-of-sync clock; retrying an old request payload after the target time passed; timezone/unit confusion producing a past ms value.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/cc9b538c4e5c60c5. Report an issue: GitHub.