Cron: jobs don't fire and nextRunAtMs silently advances
Fix cron jobs that skip runs (no execution/logs) while nextRunAtMs advances by upgrading past known scheduler regressions and verifying cron storage + timezone.
Symptoms
- A cron job looks enabled and has a valid
nextRunAtMs, but it never executes. - At the scheduled time:
- no logs appear for the run,
openclaw cron runs --id <job-id>stays empty,- and
nextRunAtMsadvances to the next occurrence anyway (as if it ran).
- Variant: cron jobs fire on gateway start (startup catch-up), then stop firing entirely until the next service restart.
Cause
This is usually caused by a Gateway cron scheduler regression in some OpenClaw versions where maintenance logic can advance a past-due nextRunAtMs without executing the run.
It can also be caused by mismatched assumptions around timezones (host timezone vs explicit tz).
Fix
1) Upgrade OpenClaw and restart the gateway
Upgrade to the latest OpenClaw (or at least a version that includes the cron scheduler fixes), then restart:
openclaw --version
# npm global install
npm update -g openclaw
openclaw gateway restart
2) Verify cron storage + job state
List jobs and confirm the scheduler sees it as enabled with a future nextRunAtMs:
openclaw cron list
openclaw cron runs --id <job-id>
If the job is supposed to run at a specific wall-clock time, set an explicit timezone:
openclaw cron edit <job-id> --tz "America/Los_Angeles"
3) If you need reliability right now, use an external scheduler as a stopgap
If you are hitting the “runs only at startup catch-up” variant, treat it as a scheduler stall until proven otherwise.
Operational fallback options:
- systemd timer / Task Scheduler calls
openclaw cron run <job-id>on your desired cadence, or - move the critical work to heartbeat (main session) so it runs on a known interval.
Verify
- Wait for the next scheduled time and confirm the job appears in:
openclaw cron runs --id <job-id>
- If it is a main-session job, confirm the system event is delivered.
- If it is an isolated job, confirm it produces an isolated run and (if enabled) delivery happens.
Related
- OpenClaw docs: Cron Jobs
- OpenClaw CLI:
openclaw cron - GitHub: #12502, #43937