Repository navigation
Description
Problem
On Windows with Microsoft Defender at its default settings, project search (Ctrl+Shift+F) does not work for the first several minutes in a repository of ordinary size, and that repeats after every Defender definition update and in every new clone or worktree.
The first search waits 16 s and then shows "Failed to search workspace contents in ''." Every further search fails within 10 ms. On this repository (32,281 files, 378 MB of working tree) the content index needs 3.6 minutes to build the first time, because Defender scans each file the first time the server reads it; the server gives the build 15 seconds. The same build takes 7 s in WSL on the same machine, 1.9 s with Defender's real-time scanning off, and git grep answers the same query over the same working tree in 2.4 s with Defender on.
Steps to reproduce
- Windows with Defender real-time protection on and no exclusion for the project folder.
- Clone a repository with a few thousand files or more (I used this repository) and add it as a project on the Windows backend.
- Open a thread, press Ctrl+Shift+F and type a word that occurs in the code.
- After about 16 s the dialog shows "Failed to search workspace contents". Try again every 30 s.
To repeat it on a repository that already works, run Update-MpSignature in an elevated PowerShell (Defender does this on its own several times a day) and search again.
Numbers
Windows Server 2025, 4 vCPU (Xeon Platinum 8473C), 16 GB, NTFS, Node 26.13.1, Git for Windows 2.56.1, commit a62bc81. The Linux baseline is WSL2 Ubuntu 24.04 (ext4) on the same machine. Defender CPU is the processor time of MsMpEng.exe during the build.
Time for the content index to become ready, created the way the server creates it (FileFinder.create({ basePath, disableMmapCache: true, disableContentIndexing: false, aiMode: false }), then waitForIndexReady), on this repository:
| Where | First build | Built again straight away |
|---|---|---|
| Windows, fresh clone, Defender default | 221.7 s (Defender CPU 436 s) | 2.6 s |
| Windows, an older clone whose files had not been read yet | 175.8 s | 2.6 s |
Windows, same clone after Update-MpSignature |
218.5 s (Defender CPU 432 s) | 2.9 s |
| Windows, fresh clone, real-time scanning off | 1.9 s | 1.7 s |
| Windows, fresh clone, folder in Defender's exclusions | 7.0 s | 2.4 s |
| WSL, same repository | 6.9 s | 0.7 to 0.8 s |
Smaller trees on a second machine of the same kind, fresh copies, Defender default: 2,043 files (29 MB) 10.8 s; 1,640 files 8.9 s; 1,373 files 7.2 s. That is about 5 ms per file, so roughly 2,800 files are enough to pass the 15 s limit.
The cost is Defender scanning files on their first read by the server process, not the index library. After resetting Defender's cache each time:
| Reading the same 32,321 files | Time | Defender CPU |
|---|---|---|
| Content index build | 212.9 s | 415 s |
Plain fs.readFile from Node, 8 at a time |
127.3 s | 482 s |
| The same Node reads a second time | 2.1 s | 1.4 s |
git grep -l "bench edit" (text that exists only in uncommitted edits, 360 files matched) |
2.4 s | 1.6 s |
git grep --untracked -l |
2.5 s | 1.1 s |
Reads made by git.exe are not charged by Defender and do not warm its cache either: the Node read pass that ran straight after the git grep runs still took 127 s.
In the app (dev server from source, Windows backend, fresh clone, headless Chromium), one search attempt about every 33 s:
| Result | |
|---|---|
| This commit | first attempt fails after 16.1 s; 13 further attempts through 450 s each fail in 0 to 10 ms |
| This commit, no attempts for 16.8 minutes after the first failure | the next search answers in 4.2 s |
| With the server and web changes of #33138 applied | attempts fail through 217 s; the attempt at 250 s answers in 294 ms |
| WSL backend, same repository | first search answers in 2.1 s |
The file picker (Ctrl+P) is not affected: its paths-only index is ready in 0.34 to 0.37 s on Windows.
Where it comes from
apps/server/src/workspace/WorkspaceSearchIndex.ts: the content variant builds an index that reads every file (createFinder, line 495) and make (line 546) fails with WorkspaceSearchIndexScanTimedOut when waitForIndexReady passes WORKSPACE_INDEX_SCAN_TIMEOUT (15 seconds, line 47). The failed entry stays in WorkspaceSearchIndexMap until it has been idle for 15 minutes, which is why retries fail instantly.
#33138 makes the search recover once the build finishes. It does not shorten the build, so with it a Windows user still has no project search for about four minutes after each definition update, with Defender using a full core or more for that time.
Expected
Project search on Windows answers in seconds on the first use in a repository, as it does in WSL, and does not stop working after a Defender definition update.
git grep shows this is reachable on the same machine with Defender on: 2.4 s for the first search over the same working tree, uncommitted and untracked files included.
Not covered
Whether a reboot alone resets Defender's cache was not measured.
Activity
juliusmarminge commentedon Nov 15, 2026
One more data point: the shipped desktop build pays the same cost, so this is not specific to running the server from source under
node.exe.Reading the same 32,321 files with plain
fs.readFileunder the desktop app's own binary (T3 Code (Nightly).exe0.1.10-nightly.20261114.4709 withELECTRON_RUN_AS_NODE=1, the way the app starts its server), straight after Defender's cache was reset: 127.9 s, Defender CPU 480 s. Undernode.exeit was 127.3 s and 482 s. Both binaries are code-signed.