fix: prevent cron from permanently dying after handler errors#16219
Merged
fix: prevent cron from permanently dying after handler errors#16219
Conversation
AlessioGr
commented
Apr 9, 2026
Contributor
📦 esbuild Bundle Analysis for payloadThis analysis was generated by esbuild-bundle-analyzer. 🤖
Largest pathsThese visualization shows top 20 largest paths in the bundle.Meta file: packages/next/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js
Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js
Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js
Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js
DetailsNext to the size is how much the size has increased or decreased compared with the base branch of this PR.
|
DanRibbens
reviewed
Apr 9, 2026
Contributor
DanRibbens
left a comment
There was a problem hiding this comment.
One question about the updated croner major version.
DanRibbens
approved these changes
Apr 14, 2026
Contributor
|
🚀 This is included in version v3.83.0 |
milamer
pushed a commit
to milamer/payload
that referenced
this pull request
Apr 20, 2026
…dcms#16219) When any error occurs during a cron job, the cron stops firing permanently and never recovers until the process is restarted. This happens because of a bug in croner v9 combined with missing error handling in the cron setup. Croner's `protect: true` option sets an internal `blocking` flag before running the handler and only clears it after the handler completes. In v9, if the handler throws, the flag is never cleared because there's no `try/finally` around it. Every future cron sees `blocking = true` and skips, so the cron is dead forever. Even without the croner bug, the error from the handler was propagating as an unhandled promise, which can crash the process. One example where this affected us was if there is a db error while running the job. Even if that error is temporary and would be resolved during the next run, the cron job would no longer run until the process is restarted. ## Two changes fix this: - Bumped croner from v9 to v10, which wraps the handler in `try/finally` so the `blocking` flag is always cleared regardless of whether the handler throws. This was a confirmed bug in v9 - Added the `catch` option to the Cron constructor, which tells croner to catch handler errors and pass them to a callback instead of letting them escape as unhandled rejections. The callback logs the error via `payload.logger.error` so it's visible in application logs. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1213983836266032
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When any error occurs during a cron job, the cron stops firing permanently and never recovers until the process is restarted.
This happens because of a bug in croner v9 combined with missing error handling in the cron setup. Croner's
protect: trueoption sets an internalblockingflag before running the handler and only clears it after the handler completes. In v9, if the handler throws, the flag is never cleared because there's notry/finallyaround it. Every future cron seesblocking = trueand skips, so the cron is dead forever.Even without the croner bug, the error from the handler was propagating as an unhandled promise, which can crash the process.
One example where this affected us was if there is a db error while running the job. Even if that error is temporary and would be resolved during the next run, the cron job would no longer run until the process is restarted.
Two changes fix this:
Bumped croner from v9 to v10, which wraps the handler in
try/finallyso theblockingflag is always cleared regardless of whether the handler throws. This was a confirmed bug in v9Added the
catchoption to the Cron constructor, which tells croner to catch handler errors and pass them to a callback instead of letting them escape as unhandled rejections. The callback logs the error viapayload.logger.errorso it's visible in application logs.