Skip to content

fix(tasks,runners): prevent duplicate dispatch and reconcile leaks#4027

Draft
cursor[bot] wants to merge 2 commits into
developfrom
cursor/critical-bug-investigation-7bbe
Draft

fix(tasks,runners): prevent duplicate dispatch and reconcile leaks#4027
cursor[bot] wants to merge 2 commits into
developfrom
cursor/critical-bug-investigation-7bbe

Conversation

@cursor

@cursor cursor Bot commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Critical runner/task lifecycle bugs were still present on develop after PR #4023 (parallel git lock). This PR cherry-picks the validated fixes from the prior investigation.

Bugs fixed

1. Duplicate dispatch on ErrAllRunnersBusy (critical)

When every runner is at capacity, RemoteJob.Run returns ErrAllRunnersBusy. The requeue path enqueued the task while it still sat in the running/active sets, then sent EventTypeRequeued from a defer. A concurrent queue tick could ClaimAndDequeue the task in that window and start a second dispatch on the same TaskRunner.

Fix: Call onTaskStop before enqueueing and emit EventTypeRequeued synchronously (matching reconciler requeue paths).

2. Stopped → failed after emergency stop on runner (critical)

When the server sends terminated_jobs and the runner emergency-stops a job, Run() unwinds with an error. The error path overwrote an already-set stopped status with failed.

Fix: Add finalizeAfterRun that respects an already-finished status.

3. HA pool state leak in failTaskRunnerLost (critical in HA)

When the reconciler won the finalize lock but the DB already had a terminal status, it returned early without releasing pool/Redis state or completing autorun.

Fix: Complete finalization when DB status is already terminal; harden offline requeue with pre-mutation DB re-check and rollback on persist failure.

Validation

  • go test ./services/tasks/... ./services/runners/... — all pass

Recent commits inspected (no new bugs)

Still open elsewhere

Open in Web View Automation 

cursoragent and others added 2 commits July 11, 2026 11:03
When every runner is at capacity, ErrAllRunnersBusy requeued tasks by
enqueueing them while they still sat in the running set, then sending
EventTypeRequeued from a defer. A periodic queue tick could
ClaimAndDequeue the task in that window and start a second dispatch on
the same TaskRunner.

Release running/active bookkeeping before enqueueing and notify the pool
synchronously, matching the reconciler requeue paths.

Co-authored-by: Denis Gukov <fiftin@outlook.com>
…finalization

Runner client: when applyTerminatedJobs emergency-stops a job and Run()
unwinds with an error, the error path overwrote stopped with failed.
Add finalizeAfterRun that respects an already-finished status.

HA reconciler: when failTaskRunnerLost wins the finalize lock but the DB
already has a terminal status, complete finalization instead of bailing
so pool/Redis state and autorun children are not leaked. Harden offline
requeue with a pre-mutation DB re-check and rollback on persist failure.

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