[BUG] Startup FileIndex blocks the event loop for ~41s when cwd is a large non-git directory (home dir, ~65k files)
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 <sid> --name "<name>" --model <model> "<initial prompt>" 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
- 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-historydoes it). Make sure the index cache is cold (first launch from that cwd, or after a while). cdinto it and runclaude --debug -r <existing-session-id>(a plainclaude --debugshows the same[FileIndex]timings; the resume just makes the freeze more visible because an initial prompt is waiting).- Try to type. Observe
[event-loop-stall]lines and[FileIndex] cache refresh completed in <tens of seconds>in~/.claude/debug/<sid>.txt; input is accepted only after the refresh completes. - Add a
.ignorein 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
.ignorefixed it fleet-wide; users who launch Claude from~on a normal workstation will hit smaller versions of the same cost as~/.claude/file-historygrows.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗