Skip to content

fix(cron): run due jobs before recomputing next run times#9755

Closed
AI-Chef wants to merge 1 commit intoopenclaw:mainfrom
AI-Chef:fix/cron-run-due-jobs
Closed

fix(cron): run due jobs before recomputing next run times#9755
AI-Chef wants to merge 1 commit intoopenclaw:mainfrom
AI-Chef:fix/cron-run-due-jobs

Conversation

@AI-Chef
Copy link

@AI-Chef AI-Chef commented Feb 5, 2026

Problem

The cron scheduler was not executing due jobs because ensureLoaded() with forceReload would call recomputeNextRuns() before runDueJobs(), pushing all overdue job times into the future.

This caused cron jobs to never execute - every time the timer fired, the due jobs would have their nextRunAtMs recalculated to a future time before they could be run.

Solution

  • Add skipRecompute option to ensureLoaded()
  • Restructure onTimer() to:
    1. Load jobs without recomputing next run times
    2. Run due jobs first
    3. Then recompute next run times for the next cycle

Testing

Verified that cron jobs now execute correctly at their scheduled times.

Greptile Overview

Greptile Summary

This PR fixes a logic bug in the cron scheduler where a forced reload (ensureLoaded({ forceReload: true })) recomputed nextRunAtMs before executing overdue jobs, effectively pushing all “due” jobs into the future and preventing them from ever running.

Changes:

  • src/cron/service/store.ts: extends ensureLoaded() with an optional skipRecompute flag to allow callers to load the store without immediately recalculating next run times.
  • src/cron/service/timer.ts: restructures onTimer() to (1) reload the store without recomputing next runs, (2) run due jobs, and then (3) recompute next runs and persist, ensuring overdue jobs execute before their schedule is advanced.

This fits with existing behavior where job state (including state.nextRunAtMs) is persisted in the cron store and recomputation is used to keep schedules accurate across restarts and long-running processes.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • The change is narrowly scoped to cron load/timer ordering, preserves default behavior for existing callers, and directly addresses a clearly described scheduler logic bug without altering external interfaces beyond an additive optional flag.
  • No files require special attention

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

The cron scheduler was not executing due jobs because ensureLoaded()
with forceReload would call recomputeNextRuns() before runDueJobs(),
pushing all overdue job times into the future.

Fix by adding skipRecompute option to ensureLoaded() and restructuring
onTimer() to:
1. Load jobs without recomputing
2. Run due jobs first
3. Then recompute next run times for the next cycle
@tyler6204
Copy link
Member

Closing as duplicate of #9823, which includes the same fix along with comprehensive tests. Note that #9589 was also opened earlier with a similar approach using defensive measures. Thanks for the contribution!

@tyler6204 tyler6204 closed this Feb 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants