CLI runs its session-teardown path mid-tool-call; the lost result is mislabelled toolDenialKind:"user-rejected" (VSCode ext 2.1.220, Windows) — same class as #83358, different trigger

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 3, 2026

Bug description

Same failure signature as #83358 / #27203 / #37442 / #40241 — is_error:true, toolDenialKind:"user-rejected", "The user doesn't want to proceed with this tool use… STOP what you are doing and wait for the user to tell you how to proceed." — but from a different trigger, a different platform/entrypoint, and with a piece of evidence I haven't seen in the existing reports: the extension log shows the CLI process running its own graceful teardown mid-tool_dispatch. This is a process-level exit, not only transport/permission-state corruption.

Environment is the VSCode extension on Windows, interactive (not the headless SDK), and the denial fires 2.4 s into the call — long before any timeout watchdog could be involved.

The log evidence

Extension log …/logs/20260803T080106/window1/exthost/Anthropic.claude-code/Claude VSCode.log, lines 18572-18586, consecutive:

10:04:36.859 [INFO]  [Stall] tool_dispatch_start tool=PowerShell toolUseId=toolu_01TdcMQ… permissionDecisionMs=963
10:04:39.287 [DEBUG] LSP server manager shut down successfully          <- CLI TEARDOWN BEGINS
10:04:39.289 [DEBUG] Cleaned up session snapshot: …\snapshot-bash-1785733404169-xii9v3.sh
10:04:39.301 [WARN]  [Stall] tool_dispatch_end tool=PowerShell … outcome=error durationMs=2440
10:04:39.301 [DEBUG] PowerShell tool error (2440ms): Shell command failed
10:04:41.204 [info]  Closing Claude on channel: q1xdnaqxtb
10:05:03.241 [info]  Launching Claude on channel: x4hvibni15 … resume: <session-id>

Two things worth flagging:

  1. **The teardown starts before the tool is marked failed** (10:04:39.287 vs …301), so the shutdown caused the tool loss, not the reverse. The child shell died with its parent → Shell command failed.
  2. Cleaned up session snapshot appears exactly once in the entire 2.1 MB log file — at this moment. This is the normal end-of-session cleanup path running mid-turn. The channel q1xdnaqxtb had been alive 2 h 02 m at that point.

The transcript then records the lost in-flight tool as:

"toolUseResult":"User rejected tool use","toolDenialKind":"user-rejected",
"userType":"external","entrypoint":"claude-vscode","version":"2.1.220"

Ruled out, from the same log

| Suspect | Evidence against |
|---|---|
| The user pressed stop / rejected | cancel_request appears 10× in the log; none within 24 minutes. Zero webview messages between tool_dispatch_start and the teardown. |
| A permission deny rule | Only rules present are 7 unrelated MCP write denies; user settings have defaultMode: bypassPermissions. |
| A PreToolUse hook | Hooks: Found 0 total hooks in registry — logged 506 times. Registry empty all session. |
| A slow/stuck permission prompt | permissionDecisionMs=963, ~p90 of 603 decisions that session (median 11 ms, p95 1205 ms). Unremarkable. |
| Parallel tool use | Single tool_use in the assistant message, stop_reason: tool_use. |
| Crash / OOM | No crash markers, no FATAL ERROR/heap messages, zero Windows Application Error or Critical events in the window. Clean shutdown path. |
| A timeout firing | Denial at 2.4 s. (Two of my three occurrences died at 3.4 s and 4.8 s.) |

What correlates: the declared timeout on a foreground shell call

I parsed every shell tool call across 13 session transcripts on this machine — 1,165 calls, 3 losses:

| how the call was issued | calls | lost | rate |
|---|---:|---:|---:|
| run_in_background: true | 85 | 0 | 0.0% |
| foreground, default 120 s | 957 | 0 | 0.0% |
| foreground, timeout < 300 s | 56 | 0 | 0.0% |
| foreground, timeout 300-599 s | 52 | 0 | 0.0% |
| foreground, timeout ≥ 600 s | 15 | 3 | 20.0% |

All three losses land in the same 15-call bucket. C(15,3)/C(1165,3) ≈ 1.7e-6 by chance.

Note this is the declared timeout parameter, not elapsed time — elapsed time is explicitly not the trigger, since two of the three died in under 5 s while a genuine 601 s foreground call in the same bucket completed fine. That's a different correlate from #83358 (where the 120 s watchdog firing is the trigger), which is why I'm filing separately rather than commenting there.

All three losses were also the same kind of command: a long-running suite whose output is piped through a tail filter (… 2>&1 | Select-Object -Last N).

Steps to reproduce

I do not have a deterministic repro — it's ~20% in the risky bucket. What reliably sets it up:

  1. VSCode extension, interactive session, let it run long (mine was 2 h 02 m / ~517 k tokens when it died).
  2. Issue a foreground Bash/PowerShell call with an explicit long timeout (600 000-900 000 ms) running a heavy suite piped through a tail filter.
  3. Some fraction of the time the CLI tears down a couple of seconds in and the tool result comes back as a user rejection.

Expected behavior

Two separate asks:

  1. The CLI should not run its session-teardown path mid-turn with no cancel request, no crash, and no user action.
  2. Even if it does, a tool result lost to process teardown must not be reported as toolDenialKind: "user-rejected". These are different events and shouldn't share a code. The mislabel is independently costly: it tells the model "STOP and wait for the user", and it tells the user that they rejected something they didn't touch. Every one of my four incidents was initially misdiagnosed because of this label. A distinct kind (transport-lost, session-terminated, …) would make it self-diagnosing.

Environment

  • Claude Code VSCode extension 2.1.220 (anthropic.claude-code-2.1.220-win32-x64) — confirmed the latest published version (marketplace lastUpdated 2026-07-25)
  • Bundled CLI resources/native-binary/claude.exe2.1.220 (extension and CLI aligned; no PATH CLI involved)
  • VS Code 1.131.0, Windows 11 Pro 26200, x64
  • entrypoint: claude-vscode, interactive, permissionMode: default
  • Occurrences: 2026-07-26 (×5, extension 2.1.220), 07-31, 08-01, 08-03

Related

  • #83358 — same signature, timeout-watchdog auto-backgrounding trigger, headless SDK on macOS
  • #27203, #37442, #40241 — the original internal-deny class
  • #57101 — "Bash tool rejected as user denied with no permission prompt" (Windows ARM64)
  • #78562 — approved tool-use prompts silently rejected, indistinguishable from a genuine decline

Workaround

Since 100 % of observed losses were foreground calls declaring ≥600 s, I added a PreToolUse hook that refuses any Bash/PowerShell call with timeout ≥ 300000 and no run_in_background, forcing long work to background + a log file. Backgrounded work leaves its output on disk, so a teardown costs a re-read instead of the whole result. That removes the shape where the losses occur; it obviously doesn't fix the teardown itself.

View original on GitHub ↗