anthropics / anthropics/claude-code
[BUG] Startup FileIndex blocks the event loop for ~41s when cwd is a large non-git directory (home dir, ~65k files)
- Ngôn ngữ chính
- Python
- Star
- 145k
- Fork
- 23.1k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
### Preflight Checklist
- [x] I have searched existing issues and this is not a duplicate. Related-but-distinct: #22041, #52995, #30302 are about large session transcripts; transcript size was explicitly tested and ruled out here (see "What's Wrong?").
- [x] This is a single bug report (the NFS watcher finding from the same investigation is filed separately).
- [x] I am using the latest version of Claude Code (2.1.258; also reproduced on 2.1.233).
### What's Wrong?
When a session starts with its working directory set to a large directory tree that is **not** a git repository, the startup `FileIndex` (the `@`-autocomplete index) falls back from `git ls-files` to `rg --files --hidden` over the whole tree and then builds the index **synchronously on the UI thread**. With cwd = the home directory (~65k files, most of them under `~/.claude/file-history`, `~/.local`, `~/.cache`), the prompt is unusable for 20–45 seconds after launch. The footer shows `/rc connecting…` the whole time, which misdirects the user toward Remote Control; RC is just waiting on the same blocked loop.
Setup: multiple long-lived sessions sharing one `~/.claude` on a shared home directory (Debian LXC). Sessions are launched by a wrapper as `claude --permission-mode dontAsk -r --name "" --model ""` in a 60×250 terminal.
Why it looked like a resume/transcript-size bug at first: brand-new sessions were unaffected. That turned out to be a warm index cache (a fresh session logged `[FileIndex] cache refresh completed in 612ms`), not transcript size. Ruled out on the real launch path, one variable at a time: Remote Control off (same stalls), removing 363 accumulated `custom-title` records (same), moving `file-history`/`subagents`/`tool-results` aside (same), re-creating the session under a new id with the same 16MB transcript (same), dropping `--name` (same), different terminal (same). Only the cwd index size mattered.
The stall detector shows the thread is busy, not waiting: CPU time ≈ wall time, `majflt=0`, `blocked_write=false`, heap growing throughout.
### What Should Happen?
The prompt should be interactive within a couple of seconds of launch regardless of how large the working directory is. The file index should be built off the main thread (or incrementally), be capped by file count / wall time, or be skipped with a notice for oversized non-repository trees. At minimum, common heavy dot-directories (`.cache`, `.local`, `.claude/file-history`) should not be indexed when cwd is a home directory.
### Error Messages/Logs
From `claude --debug` on the real launch (2.1.258):
```
[FileIndex] getProjectFiles called, respectGitignore=true
[FileIndex] getFilesUsingGit called
[FileIndex] not a git repo, returning null
[FileIndex] git ls-files returned null, falling back to ripgrep
[FileIndex] ripgrep: 54098 files in 12076ms
[FileIndex] cache refresh completed in 41278ms
[event-loop-stall] blocked for 4554ms monotonic (wall drift 4555ms, clock jump 1ms, expected 200ms). Total stalls: 3, cumulative: 7159ms monotonic / 7160ms wall blocked_write=false sigcont=false cpu=5116ms majflt=0 rss=569MB heap=170MB ext=79MB
[event-loop-stall] blocked for 8051ms monotonic (wall drift 8051ms, clock jump 0ms, expected 200ms). Total stalls: 4, cumulative: 15210ms monotonic / 15211ms wall [likely sleep/wake] blocked_write=false last_write=0ms/6989B sigcont=false cpu=7948ms majflt=0 rss=705MB heap=263MB ext=94MB
[event-loop-stall] blocked for 12007ms monotonic (wall drift 12007ms, clock jump 0ms, expected 200ms). Total stalls: 5, cumulative: 27217ms monotonic / 27218ms wall [likely sleep/wake] blocked_write=false last_write=0ms/6873B sigcont=false cpu=14487ms majflt=1 rss=1016MB heap=363MB ext=95MB
[event-loop-stall] blocked for 11146ms monotonic (wall drift 11146ms, clock jump 0ms, expected 200ms). Total stalls: 6, cumulative: 38363ms monotonic / 38364ms wall [likely sleep/wake] blocked_write=false last_write=0ms/199B sigcont=false cpu=11182ms majflt=0 rss=1234MB heap=473MB ext=119MB
high write ratio: blit=0, write=14613 (100.0% writes), screen=60x250
```
Seven stalls, 41.3s blocked in the first 45s. (The `[likely sleep/wake]` tag is a misclassification — cpu ≈ wall on every one.)
What the walk actually covered (`rg --files --hidden ~`): 64,779 files — `.claude` 32,426 (of which `file-history` 24,011), `.local` 17,423, `.cache` 11,263, `.npm` 1,465. Plain `rg --files ~` (no hidden) is 783 files.
**Workaround (confirmed):** a `.ignore` file in cwd — the index honors ripgrep's `.ignore` — listing `.claude/ .local/ .cache/ .npm/ .npm-global/ .config/ node_modules/`. Same session, same launcher afterwards:
```
[FileIndex] ripgrep: 824 files in 409ms
[FileIndex] cache refresh completed in 555ms
```
41,278ms → 555ms, zero event-loop stalls, prompt interactive immediately.
### Steps to Reproduce
1. Pick or create a directory that is not a git repository and contains ~50k+ files (a populated home directory with `~/.cache`, `~/.local`, and a used `~/.claude/file-history` does it). Make sure the index cache is cold (first launch from that cwd, or after a while).
2. `cd` into it and run `claude --debug -r ` (a plain `claude --debug` shows the same `[FileIndex]` timings; the resume just makes the freeze more visible because an initial prompt is waiting).
3. Try to type. Observe `[event-loop-stall]` lines and `[FileIndex] cache refresh completed in ` in `~/.claude/debug/.txt`; input is accepted only after the refresh completes.
4. Add a `.ignore` in that directory excluding the heavy dot-directories and relaunch: the index completes in well under a second and there are no stalls.
### Claude Model
Other — Fable 5.1 (`claude-fable-5-1`); also reproduced on Opus 5. Model is irrelevant to the stall (it happens before the first request).
### Is this a regression?
I don't know — reproduced identically on 2.1.233 and 2.1.258, so not a 2.1.258 regression.
### Last Working Version
n/a
### Claude Code Version
2.1.258 (Claude Code) — native installer. Also 2.1.233.
### Platform
Anthropic API (claude.ai subscription sign-in)
### Operating System
Ubuntu/Debian Linux — Debian 13 in an LXC container, 8 cores, no cgroup CPU throttling, PSI cpu/memory 0.
### Terminal/Shell
bash; xterm.js-based terminal app over SSH (60×250), also a plain SSH terminal — no difference.
### Additional context
- The freeze scales with terminal size as well (full-screen repaints during the index build: `write=14613 (100.0% writes), screen=60x250`).
- Because every session on this machine shares the same home directory as cwd, one `.ignore` fixed it fleet-wide; users who launch Claude from `~` on a normal workstation will hit smaller versions of the same cost as `~/.claude/file-history` grows.
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Hướng nghiên cứu
Start by reproducing from a cold cache in a large non-git directory with `claude --debug`, then inspect the `[FileIndex]` timings and event-loop stall logs. Compare `rg --files --hidden` with the `.ignore` workaround and determine where the synchronous cache refresh blocks input. Done means startup remains interactive for oversized trees and the index behavior is covered by an appropriate regression check.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- git, linux
- Lĩnh vực
- cli, performance
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 48/100