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
The PowerShell language server (PSES) recursively enumerates the whole workspace on startup looking for .ps1/.psm1/.psd1 files, and follows directory symlinks with no containment or cycle check. A single symlink pointing to the filesystem root — e.g. a standard Wine prefix inside the workspace, which always contains dosdevices/z: -> / — makes the walk effectively unbounded: PSES pins one CPU core at ~100% indefinitely and floods its log (~5 MB every 1–2 minutes) with Could not enumerate directories in the path '…' due to the path not being accessible warnings. Removing the symlink is not enough by itself — the already running enumeration never finishes; the session has to be restarted.
Environment
Extension version: 2025.4.0
VS Code version: 1.140.0
OS version: macOS 26.7.1, arm64
PowerShell version: 7.6.6
Repro
Put a standard Wine prefix inside the workspace (any folder where one runs/tests Windows tools; it does not even need to be tracked by git — the enumerator does not consult .gitignore):
WINEPREFIX=<workspace>/Temp/wine-prefix wineboot --init
ls -l <workspace>/Temp/wine-prefix/dosdevices
# c: -> ../drive_c
# z: -> / <-- auto-created by wine for every prefix
Open the workspace in VS Code with the PowerShell extension and open any .ps1 file.
Observed
The pwsh (PSES) process sits at ~100% of one core for hours (verified over 1.5 h; it had accumulated 72 minutes of CPU time by the time I sampled it).
The PSES log rotates every 1–2 minutes at ~5 MB per file, almost entirely entries like:
<Warning>Microsoft.PowerShell.EditorServices.Services.WorkspaceService: Could not enumerate directories in the path '/Users/…/Temp/wine-prefix/dosdevices/z:/…' due to the path not being accessible
The logged paths show the walker descending into z: and re-entering the workspace through nested z: segments.
sample <pid> shows a single .NET thread-pool worker spending ~100% of its samples in filesystem enumeration syscalls: SystemNative_OpenDir / SystemNative_ReadDir / SystemNative_Stat / SystemNative_LStat.
Root cause
WorkspaceService.FollowSymlinks defaults to true, so EnumeratePSFiles() runs with ignoreReparsePoints: false and maxDepth: 64: WorkspaceService.cs.
WorkspaceFileSystemWrapper.SafeEnumerateFileSystemInfos applies only that MaxRecursionDepth cap — the mitigation added in PowerShellEditorServices#795 for Process terminated bue to StackOverFlow - process terminated with code 3221225725 #1613: WorkspaceFileSystemWrapper.cs. A depth cap bounds nesting, but not breadth or revisits: a z: -> / link lets the walker enumerate the entire filesystem, and since / contains the workspace itself, the walk re-enters dosdevices/z: again and again up to the depth limit — in practice unbounded work on a real disk.
Workaround
rm "<workspace>/Temp/wine-prefix/dosdevices/z:"
then restart the session (PowerShell: Restart Current Session or reload the window). Wine itself works fine without the Z: drive, so the symlink can safely be deleted right after prefix creation (and re-deleted whenever the prefix is re-initialized).
Possible directions
Don't follow directory symlinks whose resolved target is outside the workspace roots (containment check), and/or
detect cycles (e.g. a visited (device, inode) set for symlinked directories), and/or
default FollowSymlinks to false, or expose it as a user setting,
optionally consult .gitignore — in my case the indexer burned a core over a Temp/ folder that git itself ignores.
Type: Performance Issue
Summary
The PowerShell language server (PSES) recursively enumerates the whole workspace on startup looking for
.ps1/.psm1/.psd1files, and follows directory symlinks with no containment or cycle check. A single symlink pointing to the filesystem root — e.g. a standard Wine prefix inside the workspace, which always containsdosdevices/z: -> /— makes the walk effectively unbounded: PSES pins one CPU core at ~100% indefinitely and floods its log (~5 MB every 1–2 minutes) withCould not enumerate directories in the path '…' due to the path not being accessiblewarnings. Removing the symlink is not enough by itself — the already running enumeration never finishes; the session has to be restarted.Environment
Repro
.gitignore):.ps1file.Observed
pwsh(PSES) process sits at ~100% of one core for hours (verified over 1.5 h; it had accumulated 72 minutes of CPU time by the time I sampled it).z:and re-entering the workspace through nestedz:segments.sample <pid>shows a single .NET thread-pool worker spending ~100% of its samples in filesystem enumeration syscalls:SystemNative_OpenDir/SystemNative_ReadDir/SystemNative_Stat/SystemNative_LStat.Root cause
WorkspaceService.FollowSymlinksdefaults totrue, soEnumeratePSFiles()runs withignoreReparsePoints: falseandmaxDepth: 64: WorkspaceService.cs.WorkspaceFileSystemWrapper.SafeEnumerateFileSystemInfosapplies only thatMaxRecursionDepthcap — the mitigation added in PowerShellEditorServices#795 for Process terminated bue to StackOverFlow - process terminated with code 3221225725 #1613: WorkspaceFileSystemWrapper.cs. A depth cap bounds nesting, but not breadth or revisits: az: -> /link lets the walker enumerate the entire filesystem, and since/contains the workspace itself, the walk re-entersdosdevices/z:again and again up to the depth limit — in practice unbounded work on a real disk.Workaround
then restart the session (
PowerShell: Restart Current Sessionor reload the window). Wine itself works fine without the Z: drive, so the symlink can safely be deleted right after prefix creation (and re-deleted whenever the prefix is re-initialized).Possible directions
FollowSymlinkstofalse, or expose it as a user setting,.gitignore— in my case the indexer burned a core over aTemp/folder that git itself ignores.Happy to test a build with any of these.