You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
serve --mcp answers from a frozen index after lock contention; FileLock goes stale under a live holder; walk fallback ignores .git/info/exclude (1.6.0) #1959
Version: 1.6.0 (win32-x64), Windows 11, one large git checkout shared by 10-20 concurrent MCP sessions.
With many sessions on one checkout, another writer holding the sqlite lock is the normal state, and three behaviours combine so that serve --mcp keeps answering from an index that stopped updating, and concurrent syncs can run away.
1. The watcher latches off after a lock contention
When another writer holds the lock past MAX_LOCK_RETRIES (5, sync/watcher.js), degrade() sets degradedReason and live watching stays off until start() runs again, i.e. for the rest of the session. Queries keep answering under the "CodeGraph auto-sync is DISABLED" banner. We saw codegraph_explore return unrelated symbols from the frozen index for a question about code edited after the latch.
2. FileLock treats a live holder's lock as stale after 2 minutes
FileLock.STALE_TIMEOUT_MS is 2 minutes, "regardless of PID status" (utils.js), and the holder never refreshes the lock. Every sync that runs longer than 2 minutes is joined by the next one, and the two write the same rows at once. In one case a sync ran 75 minutes at about 4 GB of RAM and read about 100 GB and wrote about 83 GB while newer syncs deleted the rows it kept adding.
3. The filesystem-walk fallback ignores .git/info/exclude and core.excludesFile
getGitChangedFiles gives git status 10 s and getGitVisibleFiles gives git ls-files 30 s. On a busy machine both can time out, and scanDirectory then walks the tree reading .gitignore files only. Directories excluded through .git/info/exclude (in our case removed git worktrees' untracked leftovers) were indexed: 47.5K files and 705K symbols, 64% of the graph, a 7.5 GB db plus a 10.7 GB WAL. Related but distinct: #1728 (watcher scope vs indexer scope), #1361 (silent sync on lock failure).
Asks, cheapest first
Report index freshness as data (index timestamp, count of tracked files modified since), not only as a prose banner.
Refuse, naming the files, when the files a query answers from changed after the last sync.
Re-arm the watcher on the next call instead of latching degradedReason until start().
Queue behind the lock for a writer that is mid-commit instead of giving up after the retry budget.
Make the walk fallback honour .git/info/exclude and core.excludesFile, as git does.
Have a live lock holder refresh its lock, or make the stale test PID-only.
We work around 1 and 3 locally (a hook that resyncs when an answer carries the DISABLED banner and prints a freshness line), and around 2 and 5 by never starting a second sync while the lock names a live PID and by adding the excluded path to .gitignore. Happy to test a fix.
Thanks for the detailed report, @LunarWerxs, and thanks for using CodeGraph! This is fixed on main by #2043, which builds on @danusha2345's #1978 (thank you!): a live writer's lock is no longer treated as stale after two minutes, the file watcher re-arms and reconciles after lock contention instead of leaving serve --mcp on a frozen index (answers that would show changed files say so instead of guessing), and the non-git scan fallback keeps honoring .git/info/exclude and core.excludesFile. The fix will be in the next release.
Activity
fix(mcp): refuse graph answers that name a drifted file after watcher…
Merge codex/integration-all-2026-09-26 (colbymchenry#1959, colbymchen…
fix(sync): recover index freshness after lock contention (#1959) (#2043)
colbymchenry commentedon Sep 27, 2026
Thanks for the detailed report, @LunarWerxs, and thanks for using CodeGraph! This is fixed on
mainby #2043, which builds on @danusha2345's #1978 (thank you!): a live writer's lock is no longer treated as stale after two minutes, the file watcher re-arms and reconciles after lock contention instead of leavingserve --mcpon a frozen index (answers that would show changed files say so instead of guessing), and the non-git scan fallback keeps honoring.git/info/excludeandcore.excludesFile. The fix will be in the next release.