{"record":{"id":"cc9b538c4e5c60c5","repo":"HKUDS/DeepTutor","slug":"at-time-is-in-the-past","errorCode":null,"errorMessage":"'at' time is in the past","messagePattern":"'at' time is in the past","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/cron/service.py","lineNumber":157,"sourceCode":"            return int(next_dt.timestamp() * 1000)\n        except ImportError:\n            raise ValueError(\n                \"cron expressions need the 'croniter' package — \"\n                \"use an 'every' or 'at' schedule instead\"\n            ) from None\n        except Exception as exc:\n            raise ValueError(f\"invalid cron expression {schedule.expr!r}: {exc}\") from None\n\n    return None\n\n\ndef validate_schedule(schedule: CronSchedule) -> None:\n    \"\"\"Reject schedules that could never run (raises ValueError).\"\"\"\n    if schedule.kind == \"at\":\n        if not schedule.at_ms:\n            raise ValueError(\"'at' schedules need a time\")\n        if schedule.at_ms <= _now_ms():\n            raise ValueError(\"'at' time is in the past\")\n        return\n    if schedule.kind == \"every\":\n        if not schedule.every_seconds or schedule.every_seconds < 30:\n            raise ValueError(\"'every' interval must be at least 30 seconds\")\n        return\n    if schedule.kind == \"cron\":\n        if schedule.tz:\n            try:\n                from zoneinfo import ZoneInfo\n\n                ZoneInfo(schedule.tz)\n            except Exception:\n                raise ValueError(f\"unknown timezone {schedule.tz!r}\") from None\n        # Raises ValueError on bad/unsupported expressions.\n        if compute_next_run(schedule, _now_ms()) is None:\n            raise ValueError(f\"cron expression {schedule.expr!r} never fires\")\n        return\n    raise ValueError(f\"unknown schedule kind {schedule.kind!r}\")","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/cron/service.py#L139-L175","documentation":"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()).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Recompute at_ms from server time right before calling add_job (now + delta)","Add a buffer (e.g. +60s) to user-chosen times to absorb latency and clock skew","Sync system clocks (NTP) on hosts that generate at_ms"],"exampleFix":"# before\nat_ms = user_selected_epoch_ms  # may already be past\nsvc.add_job(CronSchedule(kind=\"at\", at_ms=at_ms), ...)\n# after\nimport time\nnow = int(time.time() * 1000)\nat_ms = max(user_selected_epoch_ms, now + 60_000)\nsvc.add_job(CronSchedule(kind=\"at\", at_ms=at_ms), ...)","handlingStrategy":"validation","validationCode":"import time\ndef at_in_future(s: CronSchedule, skew_ms: int = 60_000) -> bool:\n    return bool(s.at_ms) and s.at_ms > int(time.time() * 1000) + skew_ms","typeGuard":null,"tryCatchPattern":"try:\n    svc.add_job(schedule, ...)\nexcept ValueError as e:\n    if \"in the past\" in str(e):\n        schedule.at_ms = int((time.time() + 3600) * 1000)  # reschedule +1h\n        svc.add_job(schedule, ...)\n    else:\n        raise","preventionTips":["Stamp at_ms immediately before submission, not long before","Add a safety buffer to user-selected times","Keep clocks NTP-synced"],"tags":["cron","validation","time","one-shot-schedule"],"backgroundTag":"scheduled-time-in-past","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}