[BUG] Startup FileIndex blocks the event loop for ~41s when cwd is a large non-git directory (home dir, ~65k files)

Status Open
Reported on v2.1.258
Maintainer reply None cached
Activity 1 comment · opened Sep 2, 2026

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

  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 <existing-session-id> (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 <tens of seconds> in ~/.claude/debug/<sid>.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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗