Skip to content

fix(tasks): prevent duplicate dispatch on requeue race#3963

Draft
cursor[bot] wants to merge 1 commit into
developfrom
cursor/critical-bug-investigation-61a1
Draft

fix(tasks): prevent duplicate dispatch on requeue race#3963
cursor[bot] wants to merge 1 commit into
developfrom
cursor/critical-bug-investigation-61a1

Conversation

@cursor

@cursor cursor Bot commented Jun 14, 2026

Copy link
Copy Markdown

Bug and impact

When a remote task was re-queued (runner offline, dispatch lost, or all runners busy), callers enqueued it before emitting EventTypeRequeued. If an EventTypeEmpty tick was already waiting in queueEvents, handleQueue could process that tick first, ClaimAndDequeue the task while it still occupied the running/active sets, and spawn a duplicate dispatch — potentially running the same playbook twice (including on different runners).

Root cause

Re-queue paths called state.Enqueue() synchronously, then sent EventTypeRequeued to an unbuffered/buffered channel. handleQueue processes events in FIFO order, so a queued EventTypeEmpty could run between enqueue and onTaskStop, violating the invariant that only waiting, non-running tasks enter the claim loop.

Fix

  • Move Enqueue into the EventTypeRequeued handler, after onTaskStop releases running/active/claim state.
  • Remove pre-enqueue calls from TaskRunner.run (all runners busy) and both reconciler requeue helpers.
  • Extract dispatchQueueEvent for testability.
  • Add regression test TestTaskPool_RequeuedEventNotClaimedByConcurrentEmptyPass.

Validation

  • go test ./services/tasks/... -short
Open in Web View Automation 

When a task was re-queued, callers enqueued it before sending
EventTypeRequeued. If EventTypeEmpty was already waiting in queueEvents
(the 5s ticker), handleQueue could ClaimAndDequeue the task while it
still occupied the running/active sets and spawn a duplicate dispatch.

Move enqueue into the EventTypeRequeued handler so it runs only after
onTaskStop releases pool bookkeeping. Add a regression test for the
concurrent Empty+Requeued ordering.

Co-authored-by: Denis Gukov <fiftin@outlook.com>
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.

1 participant