Skip to content

fix(cron): recover-stuck-scans resets a stale scanning row without enqueueing it again - #474

Open
deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/recover-stuck-no-duplicate
Open

deanrie wants to merge 1 commit into
cursor:mainfrom
deanrie:fix/recover-stuck-no-duplicate

Conversation

@deanrie

@deanrie deanrie commented Oct 26, 2026

Copy link
Copy Markdown

What

recover-stuck-scans resets a stale scanning row to pending without enqueueing it again. Stale pending rows are enqueued as before.

Why

The drain archives a queue message only after runPluginScan has written a terminal status: applyVerdict on success, markScanFailed on fatal, retryable and MAX_ATTEMPTS paths, applyBlockedShortCircuit for blocked plugins. The one way a row stays at scanning past the threshold is a worker that died mid-run (800 s cap, OOM, platform kill). That worker never archived its message, so the message comes back when the 900 s visibility timeout expires and the minute drain re-reads it.

The cron already handles that row by resetting it to pending, and then also calls enqueuePluginScan. That puts a second message for the same plugin into plugin_scans: two full runPluginScan calls (two billable agent runs) and two applyVerdict writes racing. With VT_SECONDS (900) equal to STALE_AFTER_MS (15 min) the returning message and the cron's duplicate arrive within the same minute.

pending is different: insert, update and re-scan all catch an enqueuePluginScan failure and leave the row at pending for this cron to pick up (see the comment in insert.ts), so a stale pending row may have no message at all and re-enqueueing it is the cron's job.

Change

One file. A stale scanning row is reset and reported as action: "reset"; a stale pending row is enqueued and reported as action: "enqueued". The response adds a reset count next to retried and failed. If a reset row's message really is gone (a row from before the queue existed), the next tick sees it as stale pending and enqueues it then, so recovery moves by one tick rather than being lost.

Checked

Related: #473 removes the same duplicate from the admin re-scan buttons. Not covered here: a worker killed between applyVerdict and archivePluginScan still re-runs once when its message returns, and a stale pending row whose message is merely waiting in a backlog still gets a duplicate; both need either a scan start timestamp on the row or queue introspection.

@vercel

vercel Bot commented Oct 26, 2026

Copy link
Copy Markdown

@deanrie is attempting to deploy a commit to the Anysphere Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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