← Back to PRs

#12131: fix(cron): ensure timer callback fires for scheduled jobs

by divol89 open 2026-02-08 22:13 View on GitHub →
stale
## Description Fixes #12081: Cron scheduler setTimeout callback never fires despite cronEnabled=true and nextWakeAtMs being set. ### Root Cause The function had a bug where it would unconditionally set before checking if a new timer should be created: ```typescript if (state.timer) { clearTimeout(state.timer); } state.timer = null; // <-- THIS HAPPENS ALWAYS if (!state.deps.cronEnabled) { return; // timer stays null! } const nextAt = nextWakeAtMs(state); if (!nextAt) { return; // timer stays null! } ``` If `cronEnabled` was false or `nextWakeAtMs` returned undefined, the timer would be cleared and set to null, but no new timer would be created. ### Changes 1. **Fixed timer clearing logic**: Only clear existing timer when we're about to create a new one 2. **Maintain consistent state**: Set `state.timer = null` after `clearTimeout` 3. **Preserve existing improvements**: Keep logging and 1-minute max delay logic ### Testing The fix ensures that: - When cron is disabled, existing timers are properly cleared - When no jobs are due (`nextWakeAtMs` returns undefined), timers are cleared - When jobs are due, a new timer is always created - Timer callback will fire at scheduled time ### Checklist - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update **Wallet: BYCgQQpJTJT1odaunfvk6gtm5hVd7Xu93vYwbumFfqgHb3** <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adjusts the cron scheduler’s `armTimer` logic in `src/cron/service/timer.ts` so that an existing timeout is only cleared when appropriate and the timer state is kept consistent (`state.timer = null` immediately after `clearTimeout`). The net effect is that when `cronEnabled` is true and `nextWakeAtMs(state)` returns a timestamp, a new `setTimeout` is reliably armed (with the existing 1-minute max delay clamp preserved), fixing the reported case where the callback never fired. No additional files are touched; the change is localized to the timer arming path used by the cron service. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - The change is small, localized to `armTimer`, and directly addresses the described root cause without altering job execution semantics; it maintains prior delay clamping and logging while ensuring timers are armed/cleared consistently in each branch. - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs