Repository navigation
fix(workflows): resume a waiting step once - #565
Merged
driaug merged 1 commit intoOct 27, 2026
Merged
Conversation
handleEvent reads the executions waiting for an event once and then resumes each of them. By the time it reaches one, another delivery of the same event may already have resumed it: two notifications for the same email at once (link scanners clicking every link, repeated clicks), or the same event tracked again while an earlier wait of the loop was resumed. The step was then marked completed a second time and the execution ran its next step again, sending the follow-up email twice. A timeout firing as the event arrived had the same effect, taking both the event and the timeout branch. The wait is now claimed with an update conditional on WAITING, in handleEvent and in processTimeout, and whoever moves it out of WAITING is the one that resumes it. Cancelling the timeout job after a claim is best effort: BullMQ cannot remove a job that is already running, and that job will now find the wait claimed and stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
driaug
approved these changes
Oct 27, 2026
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.
Description
handleEventclaims a wait with anupdateManyconditional onWAITINGbefore resuming it, and skips it when the count is 0. Whoever moves the step out ofWAITINGresumes it; a second delivery finds it claimed.processTimeoutclaims the wait the same way, so the event and the timeout no longer both take their branch.handleEventwith the wait completed and the run not moved on. Now that job finds the wait claimed and stops.One narrower window is not covered: an event arriving in the milliseconds between a wait being parked and
processStepExecutionre-reading it. Happy to follow up if you want it covered too.This touches the same lines of
handleEventas #540; whichever lands second needs a small rebase, which I'll do.Type of Change
feat:New feature (MINOR version bump)fix:Bug fix (PATCH version bump)feat!:Breaking change - new feature (MAJOR version bump)fix!:Breaking change - bug fix (MAJOR version bump)docs:Documentation update (no version bump)chore:Maintenance/dependencies (no version bump)refactor:Code refactoring (no version bump)test:Adding tests (no version bump)perf:Performance improvement (PATCH version bump)Testing
WorkflowExecutionService.waitResume.test.tswith 5 tests: two deliveries at once (5 rounds), a delivery nested in the loop, the event and the timeout at once, a timeout that read the wait before the event claimed it, and a timeout job that can't be removed. All 5 fail onnextwithout the fix.nextwith the change: 1553 tests passing (1548 + 5).tsc --noEmitclean for api.Checklist
Related Issues
Closes #562
🤖 Generated with Claude Code