Startup freezes ~26s in large monorepo when cwd is a subdirectory of the repo root (synchronous path remap of all tracked files)
Environment
- Claude Code 2.1.173, native install (Bun binary), Windows 11 Pro 10.0.26200
- Repo: large monorepo — 1,276,807 tracked files (
git ls-filesoutput ≈ 120 MB)
Symptom
Launching claude (even with --bare) in a subdirectory of the repo freezes the TUI for ~26-30s right after startup: UI renders, then becomes completely unresponsive, then recovers. Launching at the repo root is fast (~2.5s index refresh, no perceptible freeze). Pure CPU during the freeze — no disk or network activity (confirmed with procmon).
Root cause
The @-mention file index ([FileIndex]) always runs git ls-files --recurse-submodules with cwd = repo root. The subprocess itself is fast (~600ms). But when the session cwd is not the repo root, the result is remapped to cwd-relative paths in a synchronous .map() over all tracked files (minified as jn_ in 2.1.173):
function jn_(H,q,K){if(K===q)return H;return H.map(($)=>{let _=ty.join(q,$);return ty.relative(K,_)})}
Two path ops + string allocation × 1.27M paths, with no yielding, blocks the event loop. Debug logs (--debug-file):
- cwd = repo root:
git ls-files (tracked) took 632ms→1276807 tracked files in 713ms→cache refresh completed in 2489ms— theK===qfast path skips the remap. - cwd = subdirectory:
git ls-files (tracked) took 597ms→1276807 tracked files in 24966ms→cache refresh completed in 27066ms— ~24s spent in the remap (the duration of thetracked files in …msline includes it).
Verified it is not GC (BUN_JSC_logGC=1 shows only sub-3ms eden collections during the freeze), not git, not MCP/plugins (--bare).
Note the surrounding pipeline is already carefully chunked (buildAsync, b1K, m1K all yield every 256 items / 4ms) — jn_ is the one stage that isn't.
Runtime factor: the Zig-era Bun makes it ~10x worse
Replicating just the remap stage on the real file list (paths.map(p => path.relative(CWD, path.join(ROOT, p))) over 1,276,807 paths, same machine):
| Runtime | Time |
|---|---|
| bun 1.3.13 (Zig, what 2.1.173 ships on) | 26.3 s |
| node v25.3.0 (V8) | 3.8 s |
| bun 1.4.0-canary.1+f8723b190 (Rust rewrite) | 2.4 s |
The Zig Bun's per-call path.relative/path.join overhead accounts for most of the absolute pain, and the Rust rewrite already fixes that — once Claude Code ships on a 1.4-line Bun, this freeze shrinks to ~3s with no code change. The synchronous remap is still worth fixing though: it blocks the event loop proportionally to repo size, and it re-runs mid-session every time .git/index mtime changes (any commit, or an IDE doing git refreshes), since startBackgroundCacheRefresh re-invokes getFilesUsingGit including the remap.
Suggested fixes
- Chunk/yield inside the remap like the neighboring stages, or
- Keep the index repo-root-relative and translate to cwd-relative at query/display time (only for the ≤15 suggestions shown), which also avoids re-remapping on every refresh.
Workaround for affected users
Launch claude from the repo root instead of a subdirectory.