Skip to content

[Bug]: Command approvals stay pending after a delegated task is cancelled #21935

Open

Description

@eowca

What happens: on 0.0.46-nightly.20261008.2819, two delegated Codex tasks were each blocked on a pending shell-command
approval. Each was cancelled by its parent agent's task_cancel call, and both runs are now interrupted. Their command
approval requests remained pending afterwards. A comparable delegated Claude task's pending file-read approval, cancelled
the same way, was cleared.

This one sample shows inconsistent cleanup between the two delegated tasks. It does not establish a Codex-only bug. The
provider-specific cause is not traced; the records were found by read-only state inspection.

Expected: when a run is interrupted or cancelled, its unresolved approval requests are withdrawn, because their provider
callbacks no longer exist. They should then no longer count as pending.

The effect on any UI view was not inspected.

Activity

  1. juliusmarminge commented on Oct 19, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for flagging the difference between the two providers. Current main explains it.

    When a provider turn ends, including when it is interrupted, the server only withdraws pending user input requests. The query that finds them is limited to that kind (apps/server/src/orchestration-v2/ProjectionStore.ts:4429-4437; apps/server/src/orchestration-v2/ProviderEventIngestor.ts:579-585, :680-681). Pending approvals are only released when the whole provider session closes or fails (apps/server/src/orchestration-v2/ProviderSessionManager.ts:630-660). Codex threads share a long lived session, so that fits the Codex approvals staying pending after the cancel. I haven't traced why the Claude approval cleared.

    None of the open pull requests around delegated cancel or approvals change this.

    Accepting this as a bug.

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 19, 2026
  3. pedrozimmermannsoares commented on Oct 21, 2026

    @pedrozimmermannsoares

    The same thing happens after t3_thread_interrupt, with a Claude child, and it is visible in the UI.

    On 0.1.2-nightly.20261021.3473 (macOS desktop), a Claude delegated child (full access) stopped on a command approval. The parent interrupted it and sent new instructions with t3_thread_send. Run 3 started and worked normally: new tool calls and renders, no new approval. The child thread still reported pendingRequestCount: 1, and the "Subagent needs approval" banner stayed on screen for the old, interrupted run. The user reported it as a new request twice.

    Related: #22925 (the parent agent gets no notice when a delegated child waits for an approval).

  4. juliusmarminge commented on Oct 22, 2026

    @juliusmarminge
    Member

    Verified the state regression on main snapshot 3b95e78: both interrupted terminal paths leave the approval pending; the completed-turn control passes. Existing fix #22023 now includes the main merge and has the bugfix label. Three focused suites pass 63 tests; targeted lint and server typecheck pass. Fresh client interaction proof remains incomplete after VM stalls and environment registration failures; the PR lists unverified surfaces. No merge performed.

  5. luquibu commented on Oct 28, 2026

    @luquibu

    Additional evidence from a Windows desktop Claude thread on T3 Code 0.1.3: the stale-approval state also occurs after the user presses Stop directly, without delegated-task cancellation.

    The user stopped a run while a command approval was pending. The run was shown as interrupted, but the approval card and Approval badge remained. The user reported that Approve, Decline, Cancel, and Always allow this session could not dismiss it. Resume Thread also failed with This thread can no longer be resumed from that run.

    Persisted state and timeline

    A read-only SQLite audit found one pending command approval belonging to run 12 and its interrupted provider turn:

    Record State
    Run, run attempt, and provider turn interrupted
    Command execution item interrupted
    Approval request pending, with no resolved timestamp
    Approval node and timeline item waiting
    Approval response capability live
    Session row present, marked ready
    Binding from that session to this thread absent

    Relevant timestamps on 2026-10-28, UTC:

    17:34:09.523  Command approval created
    17:34:35.617  Outgoing query.interrupt
    17:34:35.618  Outgoing query.close
    17:34:36.078  Provider turn becomes interrupted
    17:34:36.088  Run becomes interrupted
    17:34:36.599  thread.runtime-mode.set starts; trace result: Success
    17:34:36.626  Resume message.dispatch starts; trace result: rejection
    17:34:36.657  Session detach effect created: Runtime mode changed.
    17:34:40.257  Session detach effect completes successfully
    17:34:40.467  Approval response fails: Provider session <session> was not found.
    17:34:42.326  Same approval response failure
    17:36:09.384  Same approval response failure
    17:37:38.460  Same Resume rejection
    

    The session row was not deleted. The response-context query joins sessions to thread bindings, so the missing binding makes that session unavailable to the approval handler. The subsequent mode-change detach is a separate part of this sequence; the evidence does not attribute the missing binding directly to Stop.

    There is also a related detach cleanup omission: when the last exclusive thread is detached, it is removed from the attachment set before the session and pending-request cleanup iterate that set. This helps explain why the later detach did not retire this already-stale request. ProviderSessionManager.

    Source diagnosis on 0.1.3

    Reviewed tag v0.1.3, commit d2c440312e5f39d59827ed466838a623516454d6:

    • Both terminal provider-event paths call question-only cleanup. The underlying query filters for kind = 'user_input', excluding command approvals. ProviderEventIngestor, ProjectionStore.
    • Stop's follow-up only calls settleInterruptedRun while the run is still running. That helper also returns if the run or attempt is already terminal, before reaching approval cancellation. This permits provider termination to win the ordering and leave approvals pending. Orchestrator.
    • The existing interrupted-turn test explicitly preserves its command approval fixture. The expected behavior needs updating as part of the regression coverage. ProviderEventIngestor test.

    The ordering is supported by code and matches the incident's persisted state; a fresh provider-level reproduction was not run. The read-only audit checks the existing inconsistency, not the Stop implementation end to end.

    The question-only terminal cleanup and the already-terminal Stop guards also remain in main inspected at 886d8f681234e3d88e30e0fd803fe9ccb13b0daf. PR #22023 was still open when checked; its reported test results are separate from this triage's checks.

    Impact and regression coverage

    The screenshot confirms stale attention and approval controls. Source inspection also shows that feedback to this approval, Settle, and Snooze depend on resolving the pending request; those additional actions were not exercised on the live thread. This is not a claim that all possible continuation paths are permanently blocked or that conversation history was lost.

    Please include direct desktop Stop with Claude alongside delegated cancellation, both interrupted terminal-event shapes, multiple pending approvals, and concurrent approval responses. Cleanup should update request, node, timeline item, and response capability together while preserving other turns, already resolved decisions, and asynchronous message-mode questions. Normal root completion can leave legitimate background work, so it needs a separate control case.

    This supports #21935 and its proposed fix #22023. The client offering an ineligible Resume action is being prepared as a separate report. No repair, provider command, or database write was performed during triage.

    Environment: Windows 11, build 10.0.26200, T3 desktop with local server 0.1.3, Claude Agent SDK provider, Claude Opus 5.5. Triage CLI version was 0.1.2-nightly.20261022.3541, distinct from the affected server version.

    Prepared by Codex, GPT-6, via t3 triage, with review by 10 dedicated agents.

  6. DamonYoungWork commented on Oct 30, 2026

    @DamonYoungWork

    Pi variant of this, with a worse side effect: the Pi process itself stays alive.

    T3 Code 0.1.5 desktop (macOS), Pi 1.4.0. An auto-mode Claude parent delegated a task to the Pi provider. T3 runs Pi as approval-required in that case, so the child stopped on its first bash approval. The parent called task_cancel and got cancel_requested.

    The pi --mode rpc process for that child was still running 25 minutes later, apparently still blocked in the extension's ctx.ui.confirm. It exited only when killed by hand. So for Pi, cancellation leaves a live provider process holding the pending confirm, on top of the stale approval record. Expected: cancelling the run aborts the pending confirm (Pi's ctx.signal) and ends the Pi process, or at least its turn.

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

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions