Skip to content

[Bug]: t3_worktree_handoff hangs Antigravity (AcpAdapterV2) runs in running forever because interruptTurn Interrupt escapes RunExecutionService before writeFinalRunEvents #35825

Closed as duplicate

Description

@Andyyesiyu

Before submitting

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

Area

apps/server

Steps to reproduce

  1. Open a Git repository project in T3 Code (Desktop 0.1.7, commit 322f6544dfad) using Antigravity (AcpAdapterV2, e.g. gemini-4-carbon, full-access).
  2. On a thread rooted in the main checkout (worktreePath: null), have the agent invoke t3_worktree_handoff mid-turn with a continuationPrompt (creating a new branch/worktree).
  3. Wait for the worktree creation and observe the thread timeline, run states in SQLite, and server.trace.ndjson.
  4. (For comparison) Repeat step 2 using Cursor (@cursor/sdk).

Expected behavior

The handoff turn ends cleanly without failing or hanging, and the queued worktree-continuation run starts automatically in the newly bound worktree (as fixed for Codex in #24545).

Actual behavior

Every mid-turn t3_worktree_handoff hits a lifecycle bug in RunExecutionService + ProviderSessionManager, with provider-specific symptoms:

  • Antigravity (AcpAdapterV2, reproduced 2/2 threads): The worktree is created and the worktree-continuation run is queued (status: "queued"), but the handoff run stays status: "running" indefinitely (observed stuck for ~40 minutes and ~97 minutes across two threads) and the queued continuation never starts until the user manually clicks Stop (thread.stop).
  • Cursor (reproduced 3/3 threads): The pre-handoff run does exit and the continuation runs, but the handoff run is erroneously recorded as status: "failed" with "The provider event stream closed unexpectedly." (ProviderAdapterEventStreamError: Workspace changed.).

Root cause (verified in server traces & main source)

Looking at the server trace spans and the current code on main:

  1. Mid-turn workspace change triggers immediate session teardown:
    WorktreeMcpService.performHandoff (apps/server/src/mcp/WorktreeMcpService.ts) dispatches thread.metadata.update (binding worktreePath) while the current turn is still active, which emits provider-session.detached ("Workspace changed.") and schedules ProviderSessionManager.detach.
    (Note: because the ACP child process is killed while still awaiting the /mcp HTTP tool response, WorktreeMcpService.performHandoff itself also gets Interrupted mid-flight before setupScriptRunner completes.)

  2. releaseEntry fails subscribers and concurrently closes entry.scope:
    In apps/server/src/orchestration-v2/ProviderSessionManager.ts (releaseEntry, lines 1251–1266 on main), unless gracefulSubscribers === true, it calls failSubscribers(entry, "Workspace changed.", ...) (which causes Cursor's ProviderAdapterEventStreamError failure) and immediately calls closeScopeWithin(entry.scope, ...) to tear down the session's Effect scope.

  3. Interrupt escapes RunExecutionService before writeFinalRunEvents (why Antigravity hangs forever):
    In apps/server/src/orchestration-v2/RunExecutionService.ts (startRootRun → providerEventFiber, lines 1720–1780 on main):

    • Effect.catchCauseIf((cause) => !Cause.hasInterruptsOnly(cause), ...) (line 1720) catches the ProviderAdapterEventStreamError.
    • Because routing.rootProviderTurnId !== null && !routing.rootTurnEnded, it calls input.session.interruptTurn({ providerThread, providerTurnId: routing.rootProviderTurnId, requestRuntimeRestart: true }) (line 1736).
    • For AcpAdapterV2, interruptTurn is scoped to the provider session (entry.scope). Because releaseEntry is simultaneously closing entry.scope, AcpAdapterV2.interruptTurn immediately fails in ~0.1 ms with InterruptError: All fibers interrupted without error.
    • Critical bug at lines 1744–1751:
      Effect.catchCauseIf(
        (interruptCause) => !Cause.hasInterruptsOnly(interruptCause),
        (interruptCause) =>
          Effect.logWarning(...).pipe(Effect.as(false)),
      )
      Because this handler filters out Interrupt-only causes (!Cause.hasInterruptsOnly(interruptCause)), the Interrupt from interruptTurn escapes uncaught out of providerEventFiber's outer catchCauseIf and aborts providerEventFiber before writeFinalRunEvents (line 1772) can run.
    • As a result, no terminal run.updated event is ever written, the active run stays running forever in SQLite, and the queued worktree-continuation remains blocked until manual Stop.

(Related: #35274 reports the same infinite running symptom for Grok (AcpAdapterV2) on main; #22971 tracks the Claude Workspace changed. failure. The trace below confirms the exact mechanism is AcpAdapterV2.interruptTurn exiting Interrupted and escaping RunExecutionService.ts:1744.)

Impact

Major degradation or frequent failure

Version or commit

T3 Code (Beta) 0.1.7 (322f6544dfad); root-cause lines verified still present on main (5ceabb3fe8f7)

Environment

macOS 26.5 (25F71, arm64), T3 Code (Beta) 0.1.7 Desktop, Antigravity (gemini-4-carbon) & Cursor (gemini-4-carbon)

Logs or stack traces

01:37:41.435Z | WorktreeMcpService.handoff            | dur=3679.34ms | exit=Interrupted
01:37:41.435Z | WorktreeMcpService.performHandoff     | dur=3678.98ms | exit=Interrupted
01:37:42.040Z | GitWorkflowService.createWorktree     | dur=3035.08ms | exit=Success
01:37:45.082Z | ProviderSessionManager.detach         | dur=80.65ms   | exit=Success
01:37:45.084Z | AcpAdapterV2.interruptTurn            | dur=0.11ms    | exit=Interrupted

Excerpt from server.trace.ndjson (AcpAdapterV2.interruptTurn child of RunExecutionService.startRootRun at 01:37:45.084Z, secrets/paths redacted):

exit: {
  _tag: "Interrupted",
  cause: "InterruptError: All fibers interrupted without error
    at AcpAdapterV2.interruptTurn (apps/server/dist/runtimeLayer.mjs)
    at RunExecutionService.startRootRun
    at ProviderTurnStartService.start
    at OrchestrationEffectExecutorV2.execute"
}

Workaround

  • Start threads directly in a worktree (t3_thread_launch with workspaceStrategy) instead of handing off mid-turn.
  • If an Antigravity thread hangs in running right after t3_worktree_handoff creates the worktree, manually clicking Stop settles the stuck run and immediately unlocks the queued continuation.

Activity

  1. juliusmarminge commented on Nov 13, 2026

    @juliusmarminge
    Member

    This is the same problem as #35274, so I'm closing it in favour of that one. If it is different, say how and it will be reopened.

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