Skip to content

[Bug]: Stopping background work leaves thread stuck in "Monitoring" / "Stopping..." indefinitely #11428

Description

@dkritarth

Metadata & Triage

  • Type: Bug (bug)
  • Area: apps/server, apps/web
  • Estimated Fix Size: size:S (10–29 lines in provider interrupt / task completion cleanup)
  • Impact: Major degradation or frequent failure

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Steps to reproduce

  1. Run a thread where a background command or watch loop (e.g., monitor task, background shell, or log tail) outlives the turn.
  2. Once the assistant turn finishes and settles, the thread shows a Monitoring pill in the sidebar and a • Monitoring banner with a Stop button above the composer.
  3. Click the Stop button on the banner.

Expected behavior

The background tasks should be stopped/interrupted, task.completed or terminal status should be emitted for the running background tasks, the background liveness should clear from the thread, and the Monitoring / Stopping... banner should disappear.

Actual behavior

The button switches to Stopping..., but the thread remains stuck in Monitoring indefinitely. The Stopping... state never clears.

Screenshots

1. Thread showing "Monitoring" badge in sidebar and composer banner with "Stop" button:
Monitoring state

2. Stuck on "Stopping..." indefinitely after clicking Stop:
Stuck on Stopping

Root Cause Analysis

  1. In apps/web/src/components/ChatView.tsx, handleStopBackgroundWork calls interruptThreadTurn under the assumption that it tears down live background work across the thread:
    // Background work (subagent fleets, workflow runs, watch loops) can outlive
    // the turn; once it settles, the composer stop button is gone, so this
    // banner is the only visible stop affordance. Stop routes through the
    // stop-everything interrupt: it kills every live background task before
    // interrupting, and works by session, so no active turn is needed.
    isStoppingBackgroundWork holds "Stopping..." until activeBackgroundLiveness === null.
  2. When interruptThreadTurn is called after a turn has settled, turnId is undefined.
  3. In ProviderCommandReactor.ts, the command routes to providerService.interruptTurn({ threadId: event.payload.threadId }).
  4. If the provider adapter's interruptTurn implementation only handles active prompt execution (cancelling active generation / pending approvals) rather than terminating lingering background tasks/commands or emitting terminal task states, ThreadBackgroundLivenessService is never notified that the background task ended.
  5. Because no task.completed or terminal event is received, ThreadBackgroundLivenessService keeps the task in its monitors set.
  6. As a result, activeBackgroundLiveness remains "monitoring", the sidebar badge remains Monitoring, and the composer banner stays stuck in Stopping....

Note on provider implementation: This was observed with the Antigravity provider, where AntigravityAdapter.ts only calls context.runtime.cancel and cancels pending requests inside interruptTurn, but does not clean up context.commands or invoke finishBackgroundCommands(context).

Version or commit

Latest upstream main @ b1e223e2b (v0.0.41-nightly.20260912.1599)

Environment

Desktop / Web app

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions