Skip to content

fix(desktop): keep draining the worker queue past a pre-aborted task - #9482

Open
Bruno-rose wants to merge 2 commits into
ParadevAI:mainfrom
Bruno-rose:fix/desktop-worker-queue-pre-aborted
Open

Bruno-rose wants to merge 2 commits into
ParadevAI:mainfrom
Bruno-rose:fix/desktop-worker-queue-pre-aborted

Conversation

@Bruno-rose

@Bruno-rose Bruno-rose commented Nov 7, 2026 •

Copy link
Copy Markdown

What & why

Fixes #9463.

drainQueue rejected a pre-aborted task and moved to the next slot, so the task queued behind it waited with no timeout until another task arrived. This copies the inner loop the host-service WorkerTaskRunner already has. No current caller passes a signal, so this cannot happen today.

How I tested it

Failing regression test with the existing MockWorker. The bug was first found by checking every order of events in a small model, then reproduced against the real code.

  • WorkerTaskRunner.test.ts: 6 pass, 0 fail.

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs
  • I have signed the CLA (the bot asks on your first PR)

Summary by cubic

Fixes #9463: drainQueue rejected a pre-aborted task and moved to the next slot, leaving the next queued task stranded with no timeout until another task arrived to trigger another drain.

The slot now keeps pulling from the queue until it dispatches a task or the queue is empty, matching the inner loop the host-service WorkerTaskRunner already uses. No current caller passes a signal, so this can't happen today; a regression test covers the scenario.

Written for commit d8647ea. Summary will update on new commits.

View guided diff

drainQueue moved to the next slot after rejecting a pre-aborted task, so a
live task queued behind it stayed queued while the slot sat idle. Pull from
the queue until the slot dispatches or the queue is empty, as the
host-service runner already does. No caller passes a signal today.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 494bfdc9-609e-416c-a301-3282ce5cd3df
Commits

Reviewing files that changed from the base of the PR and between 19db630 and d8647ea.

Files selected for processing: 2

  • apps/desktop/src/lib/trpc/workers/WorkerTaskRunner.test.ts
  • apps/desktop/src/lib/trpc/workers/WorkerTaskRunner.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/lib/trpc/workers/WorkerTaskRunner.ts">

<violation number="1" location="apps/desktop/src/lib/trpc/workers/WorkerTaskRunner.ts:282">
P1: This new loop can reach an uncloneable payload after skipping a pre-aborted task, but `postMessage` throws synchronously and can crash the desktop process. Catch the dispatch error, clear `activeTaskId`, and reject the task so draining can continue.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client as Client Code
    participant Runner as WorkerTaskRunner
    participant Queue as Internal Task Queue
    participant Slot as Worker Slot
    participant Worker as Worker Process

    Note over Client,Worker: Task dispatch with pre-aborted task handling

    Client->>Runner: runTask("first", payload)
    Runner->>Queue: Enqueue first task
    Runner->>Slot: Acquire free slot (concurrency=1)
    Slot->>Worker: postMessage(task "first")
    Worker-->>Slot: Task complete
    Slot-->>Runner: Empty slot

    Client->>Runner: runTask("aborted", { signal: AbortSignal.abort() })
    Runner->>Queue: Enqueue aborted task
    Client->>Runner: runTask("third", payload)
    Runner->>Queue: Enqueue third task (behind aborted)
    
    Runner->>Runner: drainQueue() triggered
    loop While queue not empty and slot free
        Runner->>Queue: Shift next task (aborted)
        Runner->>Runner: Check abortSignal
        alt Task pre-aborted
            Runner->>Runner: rejectTask("cancelled")
            Runner->>Queue: Continue loop, shift next task
        else Task valid
            Runner->>Runner: Assign to slot, set timeout
            Slot->>Worker: postMessage(task "third")
            Worker-->>Slot: Task complete
            Slot-->>Runner: Empty slot
        end
    end

    Runner-->>Client: Resolve "first"
    Runner-->>Client: Reject "aborted" (WorkerTaskAbortedError)
    Runner-->>Client: Resolve "third" with result
Loading

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

taskType: task.taskType,
payload: task.payload,
};
slot.worker.postMessage(request);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This new loop can reach an uncloneable payload after skipping a pre-aborted task, but postMessage throws synchronously and can crash the desktop process. Catch the dispatch error, clear activeTaskId, and reject the task so draining can continue.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/desktop/src/lib/trpc/workers/WorkerTaskRunner.ts, line 282:

<comment>This new loop can reach an uncloneable payload after skipping a pre-aborted task, but `postMessage` throws synchronously and can crash the desktop process. Catch the dispatch error, clear `activeTaskId`, and reject the task so draining can continue.</comment>

<file context>
@@ -256,29 +256,31 @@ export class WorkerTaskRunner {
+					taskType: task.taskType,
+					payload: task.payload,
+				};
+				slot.worker.postMessage(request);
 			}
-
</file context>
Suggested change
slot.worker.postMessage(request);
try {
slot.worker.postMessage(request);
} catch (error) {
slot.activeTaskId = null;
this.rejectTask(
task.taskId,
new WorkerTaskError(
`postMessage failed for task "${task.taskType}": ${(error as Error).message}`,
),
);
}

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.

Desktop worker queue: a task queued behind a pre-cancelled task never runs

1 participant