Skip to content

fix(runners,tasks): critical runner/task requeue and finalization bugs#4006

Closed
cursor[bot] wants to merge 2 commits into
developfrom
cursor/critical-bug-investigation-f25e
Closed

fix(runners,tasks): critical runner/task requeue and finalization bugs#4006
cursor[bot] wants to merge 2 commits into
developfrom
cursor/critical-bug-investigation-f25e

Conversation

@cursor

@cursor cursor Bot commented Jul 5, 2026

Copy link
Copy Markdown

Summary

Daily critical-bug inspection found three pre-existing correctness bugs still present on develop (unchanged since prior automation runs). New commits since 2026-07-04 (git URL validation, access-key env filter, docker lib name) were reviewed and do not introduce new critical issues.

Bugs fixed

1. Duplicate dispatch on ErrAllRunnersBusy

Impact: When all runners are busy, a task requeued while still in the running set could be claimed by a concurrent queue tick, causing duplicate dispatch on the same TaskRunner.

Root cause: TaskRunner.run() enqueued the task before releasing running/active bookkeeping; EventTypeRequeued was sent from a defer after the race window.

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

2. Stopped → failed after terminated_jobs

Impact: When a runner emergency-stops a job via terminated_jobs and Run() returns an error, the error path overwrote stopped with failed.

Root cause: job_pool.go always set fail status on any Run() error without checking if the job was already terminal.

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

3. HA pool state leak in failTaskRunnerLost

Impact: In HA mode, when the reconciler won the finalize lock but the DB already had a terminal status, finalization bailed early without releasing pool/Redis state or completing autorun children.

Root cause: finalizeRemoteTaskLocked() only released stale pool state when End != nil inside the HA-only branch.

Fix: Always release stale pool state when End != nil; complete finalization when DB is terminal but End is unset.

Validation

  • go test ./services/tasks/... ./services/runners/... ./db/... ./db_lib/... — all pass
  • Includes new/updated tests from prior validated fix commits
Open in Web View Automation 

cursoragent and others added 2 commits July 5, 2026 11:04
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>
@fiftin fiftin closed this Jul 8, 2026
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.

2 participants