openclaw - 💡(How to fix) Fix Cron scheduler does not re-arm timers for jobs with empty state after gateway restart [1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#71387Fetched 2026-04-26 05:13:21
View on GitHub
Comments
1
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
closed ×1commented ×1

Root Cause

In jobs-DYu8cOq2.js, computeStaggeredCronNextRunAtMs correctly computes the next run time, but the scheduler's timer re-arm logic during startup appears to skip jobs with empty state. The recomputeNextRuns function (line ~463) iterates jobs but may not properly arm the underlying timer for jobs where state.nextRunAtMs was undefined before recomputation.

Code Example

10:02:34 [cron] job added: nextRunAtMs=1777084200000 (10:30:00 CST), timerArmed=true
10:06:40 [gateway] cron: job created (re-created with same schedule)
10:07:10 [gateway] signal SIGTERM received; shutting down
10:07:13 [gateway] loading configuration…
10:07:18 [gateway] ready
10:30:00NO cron trigger in logs (expected study-loop to fire)
11:10:24 — study-loop fires for the first time (skipped the 10:30 window)
RAW_BUFFERClick to expand / collapse

Bug Description

After a gateway restart (SIGTERM + systemd restart), cron jobs whose state is {} (empty — e.g. newly created jobs that haven't completed a run yet) miss their next scheduled trigger.

Steps to Reproduce

  1. Create a new cron job: openclaw cron add --name test --cron '*/30 8-22 * * *' --exact --message 'hello' --announce
  2. Note the nextRunAtMs is correctly computed (e.g. :30 of the current hour)
  3. Restart the gateway before the next trigger: openclaw gateway restart
  4. Wait for the scheduled trigger time to pass
  5. Observe: the cron does not fire at the expected time

Expected Behavior

After gateway restart, the cron scheduler should recompute nextRunAtMs for all enabled jobs and arm timers accordingly, regardless of whether job.state is empty or populated.

Actual Behavior

Jobs with empty state: {} are loaded from jobs.json but their timers are not re-armed. The job eventually fires on a later tick (e.g. the next hour boundary), but the first expected trigger after restart is silently skipped.

Evidence

Gateway logs from a real incident (2026-04-25):

10:02:34 [cron] job added: nextRunAtMs=1777084200000 (10:30:00 CST), timerArmed=true
10:06:40 [gateway] cron: job created (re-created with same schedule)
10:07:10 [gateway] signal SIGTERM received; shutting down
10:07:13 [gateway] loading configuration…
10:07:18 [gateway] ready
10:30:00 — NO cron trigger in logs (expected study-loop to fire)
11:10:24 — study-loop fires for the first time (skipped the 10:30 window)

The job's persisted state in jobs.json at the time of restart was {} (no nextRunAtMs).

Analysis

In jobs-DYu8cOq2.js, computeStaggeredCronNextRunAtMs correctly computes the next run time, but the scheduler's timer re-arm logic during startup appears to skip jobs with empty state. The recomputeNextRuns function (line ~463) iterates jobs but may not properly arm the underlying timer for jobs where state.nextRunAtMs was undefined before recomputation.

Environment

  • OpenClaw v2026.4.23
  • Node.js v24.14.1
  • Linux 6.17.0-22-generic (x64)
  • Cron schedule: */30 8-22 * * * with staggerMs: 0 (exact)

extent analysis

TL;DR

The issue can be fixed by modifying the recomputeNextRuns function to properly arm the underlying timer for jobs with empty state.

Guidance

  • Review the recomputeNextRuns function (line ~463 in jobs-DYu8cOq2.js) to ensure it correctly arms the timer for jobs with state.nextRunAtMs initially set to undefined.
  • Verify that the computeStaggeredCronNextRunAtMs function is correctly computing the next run time for jobs with empty state.
  • Check the gateway logs to confirm that the job's timer is being armed after the gateway restart.
  • Consider adding a check in the recomputeNextRuns function to handle jobs with empty state and ensure their timers are armed.

Example

// Example of how to modify the recomputeNextRuns function
function recomputeNextRuns(jobs) {
  jobs.forEach((job) => {
    const nextRunAtMs = computeStaggeredCronNextRunAtMs(job);
    if (nextRunAtMs !== undefined) {
      // Arm the timer for the job
      armTimer(job, nextRunAtMs);
    }
  });
}

Notes

The provided analysis suggests that the issue is related to the recomputeNextRuns function not properly arming the timer for jobs with empty state. However, without the full codebase, it's difficult to provide a definitive solution.

Recommendation

Apply a workaround by modifying the recomputeNextRuns function to handle jobs with empty state, as this is the most likely cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Cron scheduler does not re-arm timers for jobs with empty state after gateway restart [1 comments, 1 participants]