anthropics / anthropics/claude-code

[BUG] Each sandboxed Bash tool call blocks the event loop ~8.4s (fixed cost, independent of command); terminal input freezes for the duration

未關閉
#92,896 2 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
area:core bug has repro perf:memory platform:linux
主要語言
Python
星號
145k
分支
23.1k
PR 合併指標
PR 指標待擷取

描述

### Preflight Checklist

- [x] I have searched existing issues and this is not a duplicate. Nearest neighbours, both distinct, discussed under "Relationship to existing issues" below: #92422 (same allocation waste, no latency dimension) and #91633 (large non-git cwd — **tested and ruled out here**).
- [x] This is a single bug report.
- [x] I am using the latest version of Claude Code (2.1.263). Also reproduced on 2.1.245, .246, .252, .258 and .261.

## What's Wrong?

Every agent turn costs a **fixed 8.8–10.9 seconds** during which the `claude` process saturates one core and its RSS climbs by 1.0–1.6 GB, then sweeps back to a ~430 MB baseline. Because the main loop is single-threaded, it stops servicing stdin for that whole window: keystrokes are not echoed, queue in the PTY buffer, and then appear and execute all at once when the burst ends.

From the user's side this reads as the terminal freezing for ~10 seconds on roughly half of prompts, several times within a single typed message.

The cost does not scale with anything I can find. It is the same on a three-turn-old session as on a ten-hour one, the same on Haiku as on Opus with a 1M context, and the same on a build from before the problem started as on today's. The static context payload on this machine totals ~16 KB, so the allocation is roughly **80,000× the input it is processing**.

This is *saturation*, not a hang — the process is state `R`, transcript writes continue, and it returns to `S` on its own. It is distinct from #92222, where the loop never exits and only `kill -9` recovers.

## Environment

| | |
|---|---|
| Claude Code | 2.1.263 (mise install, `~/.local/share/mise/installs/claude/2.1.263`) |
| OS | Omarchy (Arch), kernel 7.1.9-arch1-2 |
| Hardware | Intel i7-11700, 16 cores, 31 GiB RAM |
| Terminal | foot, `TERM=xterm-256color`, bash |
| `vm.overcommit_memory` | 0 (default) |

Non-default settings that could plausibly interact: `"tui": "fullscreen"`, `sandbox.enabled: true`, `autoMode` configured, `effortLevel: xhigh`. Zero MCP servers in `.claude.json`.

## Measurements

Sampler: read every `/proc/[0-9]*/stat` whose comm is `claude` at 100 ms; log any process holding >60% CPU for ≥0.4 s together with its RSS floor and ceiling across the burst.

Interactive sessions:

```
12:13:16 pid=1031044 8.9s rss 440 -> 1793M (+1353M)
12:13:27 pid=1031044 8.8s rss 719 -> 2295M (+1576M)
12:14:59 pid=1028651 8.9s rss 703 -> 2288M (+1585M)
12:16:05 pid=1028651 9.0s rss 428 -> 1750M (+1322M)
12:16:16 pid=1028651 9.1s rss 705 -> 2289M (+1584M)
12:19:48 pid=1031044 9.1s rss 421 -> 1716M (+1295M)
12:20:14 pid=1031044 9.0s rss 450 -> 1786M (+1335M)
```

CPU time ≈ wall time throughout; the thread is busy, not blocked.

## Reproduction

A headless multi-turn run reproduces it identically — one burst per agent turn, ~45 s per trial, no interactive session needed:

```bash
claude --model opus -p 'Run these three shell commands, one per turn, waiting for each
result before the next: first `date +%s`, then `uname -r`, then `echo done`.
Report the three outputs.' \
--allowedTools "Bash(date:*)" "Bash(uname:*)" "Bash(echo:*)"
```

Positive control: headless Opus gave 9.4 / 9.7 / 9.7 / 9.6 s against 8.8–9.1 s interactive, so the headless path is representative and every result below is comparable.

## What I ruled out

Each row is a measured trial with the reproducer above, same machine, same hour (2026-09-08 12:13–13:00 ET).

| Variable | Result |
|---|---|
| **Context window / model** | Opus 1M **9.4s**/1314M · Sonnet 200k **9.8s**/1368M · Haiku **9.6s**/1395M |
| **Client version** | 2.1.245 **10.1/10.0/9.9/9.8s** · .246 **9.5/9.4/9.3/9.6s** · .252 **9.7/9.6/9.7/9.5s** · .258 **9.7/9.6/10.4/10.9s** · .261 **~9.0s** · .263 **9.0s** |
| **cwd size / git-ness** | non-git tree, 55,664 files: 9.0s · git repo, 45 files: **9.3/9.1/9.1/9.4s** |
| **Transcript size** | bursts on a 3-turn-old session; duration never drifts within a session |
| **Context payload** | ~16 KB total — CLAUDE.md 2,785 B, MEMORY.md 7,573 B, `autoMode.environment` 3,435 B, 0 MCP servers |
| **Machine resources** | load 0.71/16 cores, 18 GiB free, all three `/proc/pressure/*` counters `avg10=0.00` **during** bursts |
| **Telemetry** | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 DISABLE_TELEMETRY=1 DISABLE_ERROR_REPORTING=1` → 9.8/9.4/9.3/9.7s |
| **Concurrency** | 4 concurrent sessions on 16 mostly-idle cores; each process stalls independently |
| **Terminal multiplexer** | reproduced in a bare `foot` window with no multiplexer in the process tree |

The binary is Bun-compiled (JavaScriptCore), so `NODE_OPTIONS=--max-old-space-size` has no effect.

## On timing, stated honestly

Session transcripts carry a `"version"` field. Mine read 2.1.245 every day from 2026-08-31 through 09-04, then 2.1.261 from 09-05 onward — and I first noticed the freezes on 09-05. That correlation is real but **confounded**, because the version changed on the same day the behaviour did.

The test that separates "this build is bad" from "today is bad" is running the last-known-good build now. 2.1.245 stalls **10.1 / 10.0 / 9.9 / 9.8 s** today. So the client build is not the variable, and a rollback is not a workaround.

I want to be clear about the limit of that: my "no lag before 09-04" baseline is recollection, not instrumented data — the sampler did not exist until 09-08. What I can state as measured is that six builds spanning 2.1.245 to 2.1.263 all behave identically **today**.

## What Should Happen?

Per-turn overhead should not block terminal input. Whatever allocates ~1.4 GB each turn should either not allocate it, or do so off the thread that services stdin and drives the UI.

## Relationship to existing issues

- **#92422** (*Excessive unused memory allocation causing OOM when `vm.overcommit_memory=2`*) — likely the closest relative and possibly the same root cause seen from another angle. Their figures ("actual usage typically ~0.5 G but multiple gigabytes allocated per process") match mine almost exactly. That report has no CPU or latency dimension, which is the whole substance of this one; if they turn out to share a cause, this issue supplies the user-visible cost.
- **#91633** (*Startup FileIndex blocks the event loop when cwd is a large non-git directory*) — superficially a strong match, since my primary cwd is a non-git tree of 55,664 files. **Ruled out by measurement:** the reproducer run from a 45-file git repo still stalls 9.3/9.1/9.1/9.4 s. Theirs fires once at startup; this fires once per turn. Recording it so nobody re-treads the path.
- **#92222** (*Sessions freeze intermittently without error output*) — a non-terminating main-thread loop requiring `kill -9`. I have hit that too, separately; it is a different failure. This one always self-recovers within ~10 s.

貢獻指南

這個儲存庫沒有索引到貢獻指南

研究方向

Start by running the headless reproducer from the issue with the allowed Bash tools and observe the per-turn CPU/RSS burst. Compare it with the related reports #92422 and #91633, but keep this focused on per-turn sandboxed Bash calls blocking stdin. Done means Bash tool turns no longer add the fixed ~9s main-loop stall and terminal input stays responsive.

由索引模型根據 Issue 內容生成。

評估

技術堆疊
bash, bun, linux
領域
cli, performance
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
活躍
描述清晰度
描述清楚
新手友好度
48/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。