Skip to content

Interrupting a turn does not kill the claudeAgent child process — thread appears unstoppable #5587

Closed as duplicate

Description

@JorgeMenaDev

What happened

A thread's turn was interrupted (Stop clicked in the desktop UI), the orchestration engine settled the turn as interrupted — but the backing claude child process was never killed. It kept running and burning CPU for 15+ minutes until we killed it by hand, and the thread UI kept showing it as running, so Stop appeared broken to the user.

Evidence (from userdata/state.sqlite projections)

  • Server: 0.0.32-nightly.20260807.1022 (desktop-bundled), macOS (Darwin 25.6.0).
  • projection_turns row: turn requested 2026-08-07T07:46:12Z, state interrupted, completed 2026-08-07T08:26:38Z.
  • The claude child process for that thread (driver claudeAgent, spawned with --output-format stream-json --input-format stream-json) was still alive and consuming ~20% CPU at 08:41Z, 15 minutes after the engine settled the turn. projection_thread_sessions still showed the session as running until the process was killed manually, at which point it flipped to stopped.

Expected

Interrupting a turn should terminate (or at least signal, then escalate to kill) the driver's child process, and the session/thread UI should reflect the stop once the turn settles.

Impact

The user cannot stop a runaway thread from the UI; the only recourse is a manual kill of the right PID, which is error-prone (in our case a healthy sibling thread's process was killed as collateral while hunting for the stuck one).

We have not checked whether other drivers (codex, opencode, cursor, grok) reap their children on interrupt — may be worth auditing the same path.

Activity

  1. nhorto commented on Aug 17, 2026

    @nhorto

    Traced the mechanism for this today — posted the detail on #4713, which reports the same failure from the projection side.

    Short version: ClaudeAdapter.interruptTurn awaits query.interrupt() with no timeout. That call waits for a control-request acknowledgement over the CLI's stdin, so a wedged child never answers and the promise never settles. Since the failure is non-resolution rather than rejection, no error path runs — which is exactly why the turn settles as interrupted while the child survives and the session projection stays running.

    Your "signal, then escalate to kill" expectation is the right shape. #7349 bounds the interrupt at 10s and escalates to session teardown, which closes the query and lets the SDK take the process down.

    On your audit question for the other drivers: I looked, but did not verify them, so treat this as a starting point rather than an answer. They are structurally different — Codex delegates to a runtime layer, Cursor and Grok go through ACP cancel, OpenCode issues an HTTP session.abort — so none is the same unbounded stdin await. Whether ACP cancel can hang the same way is the open question; #6531 already applies a "settle locally, don't block on the provider call" fix to OpenCode's path.

    Re: the collateral kill while hunting for the stuck PID — the reliable discriminator is the --resume=<session id> value in the child's argv, which matches resume_cursor_json in provider_session_runtime for the thread. Matching on the worktree path is what puts sibling threads (and, if you are driving this from T3 Code itself, the agent doing the hunting) in range.

  2. t3dotgg commented on Aug 22, 2026

    @t3dotgg
    Member

    Note

    🤖 GPT-5.6 Sol in Codex responding on behalf of Theo

    Closing as a duplicate of #4713. Both describe the Claude interrupt path leaving its child process and thread active.

    Track the remaining work in #4713.

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