Skip to content

pet server configure hangs in a filesystem-scan busy-loop on large workspaces → 30s timeout × 3 retries = ~90s 100% CPU (thermal shutdown on laptops) #1747

Description

@calibrator2026

Summary

The pet binary (python-env-tools, shipped by ms-python.vscode-python-envs) busy-loops inside a filesystem stat/close scan when handling a configure JSON-RPC request whose environmentDirectories glob is evaluated against a large workspace. The scan does not complete within the extension's 30-second configure timeout, so the extension kills pet (SIGTERM), restarts it, and re-sends configure — which hangs again. After 3 attempts (~90 s of sustained 100% CPU on one core) the extension gives up.

On a thermally-constrained laptop this ~90 s 100% CPU spike pushes the package past 105 °C and the embedded controller cuts power with no OS-level log (no OOM, no panic, no thermal trip in journald — the journal just ends mid-normal-activity). The bug therefore masquerades as a hardware/BIOS issue and is very hard to diagnose. Worth fixing for that reason alone, not just the wasted CPU.

Environment

  • Extension: ms-python.vscode-python-envs v1.20.1 — the latest version compatible with my VS Code. (I have 1.22.0–1.36.0 installed too, but VS Code refuses to load them: "Extension is not compatible with Code 1.108.1. Extension requires: ^1.110.0-20260204". So I can't test newer versions without also upgrading VS Code. Happy to re-test on 1.36.0 if a maintainer confirms whether the scan/timeout behavior changed.)
  • VS Code: 1.108.1
  • OS: Ubuntu 24.04.3 LTS, kernel 6.17.0-22-generic, x86_64
  • pet binary: ELF 64-bit, static-pie, with debug_info, not stripped
  • Hardware: HP OMEN laptop, Intel Core Ultra (Arrow Lake-H) + NVIDIA RTX 5070 Laptop (degraded cooling — needs repaste)

The trigger workspace

The crash only happens for one of my open workspaces. Its size is the key:

$ find /home/<user>/workspace/large-project | wc -l
279488
$ du -sh /home/<user>/workspace/large-project
101G

279,488 files / 101 GB, including several node_modules trees with symlinks. The other workspaces I have open (346 / ~small files) configure in <1 s and never spin. The configure params the extension sends for this workspace:

{"workspaceDirectories":["/home/<user>/workspace/large-project"],
 "environmentDirectories":["/home/<user>/workspace/large-project/**/.venv"],
 "pipenvExecutable":"pipenv","poetryExecutable":"poetry",
 "cacheDirectory":".../pythonLocator"}

Note the **/.venv glob — pet walks the entire 279k-file tree looking for .venv dirs.

Reproduction (CLI, deterministic, ~12 s)

pet server reads JSON-RPC over stdio with Content-Length framing. Sending the exact configure request the extension sends hangs pet in a stat/close scan loop:

$ # request body in configure.json (the JSON above)
$ ( printf "Content-Length: %s\n\n" "$(wc -c < configure.json)"; cat configure.json; sleep 13 ) \
    | timeout -s INT 12 strace -c -f -e trace=futex,poll,ppoll,read,write,stat,newfstatat,openat,close \
      ~/.vscode/extensions/ms-python.vscode-python-envs-1.20.1-linux-x64/python-env-tools/bin/pet server
# exit code 124  (pet never responded within 12 s)

strace summary (12 s, no pet find, just configure):

% time     seconds  usecs/call     calls    errors syscall
 72.50    0.203708           1    138906           close
 27.50    0.077264          11      6792      4317 stat
  0.00    0.000000           0         1           read
  0.00    0.000000           0         1           poll
100.00    0.280972          1    145700      4317 total

So pet is in a tight stat/close loop walking the workspace, 64% of stats returning ENOENT (probing for .venv/python/etc. at every entry). 12 s wasn't enough to finish; the extension's 30 s timeout isn't either.

Extension log (from the actual crash session)

~/.config/Code/logs/20260829T164624/window2/exthost/ms-python.vscode-python-envs/Python Environments.log:

16:47:32.185 [info] [pet] Starting Python Locator .../pet server
16:47:32.186 [info] [pet] configure: Sending configuration update: {"workspaceDirectories":[".../<large-workspace>"],"environmentDirectories":[".../<large-workspace>/**/.venv"],...}
16:48:02.186 [warning] [pet] Configure request timed out, killing hung process for restart
16:48:02.186 [error]  [pet] configure: Configuration error Request 'configure' timed out after 30000ms
16:48:02.187 [warning] [pet] Restarting Python Environment Tools (attempt 3/3, waiting 4000ms)
16:48:02.188 [error]  [pet] Python Environment Tools exited unexpectedly with code null, signal SIGTERM
16:48:06.195 [info] [pet] Starting Python Locator .../pet server     # restart, send configure again
16:48:36.197 [warning] [pet] Configure request timed out, killing hung process for restart   # 30s later, again
16:48:36.197 [error]  PET failed after 3 restart attempts.
16:48:36.199 [warning] [priorityChain] .../<large-workspace> defaultInterpreterPath '/bin/python3' unresolvable, falling back to auto-discovery

Same pattern reproduces every time this workspace is opened (also seen in the 20260829T213146 session). Other workspaces configure in <1 s.

Impact / why this matters

Secondary issue: pet busy-loops on malformed input too

While figuring out the framing I sent a request without a valid Content-Length header. Instead of failing gracefully, pet entered an infinite error-spam loop writing the same error to stderr — 941,762 write calls in 12 s (~78k writes/s), 100% CPU:

% time  calls    syscall
 58%   941762   write
 24%    98908   open
 11%   188354   read
  ...

stderr was the error message recursively nesting itself. A malformed request should error once and continue, not spin. Likely the same lack of backoff/bounding that causes the configure hang.

What I'd suggest (and can help validate)

  1. Bound the configure scan: skip / prune common heavy dirs (node_modules, .git, __pycache__, .venv contents), cap depth or entry count, or make discovery incremental/lazy instead of a full synchronous walk that must finish before configure responds.
  2. Don't retry 3× with no backoff: a workspace that times out once will time out 3 times — that's 90 s of guaranteed 100% CPU. Exponential backoff or a single retry would cut the blast radius.
  3. Fix the error-spam loop on malformed input (fail once, don't busy-write).
  4. If a maintainer can tell me which pet subcommand/request path is the right one to exercise, I can attach a perf record call graph (I have perf + the binary's debug symbols ready, just need to temporarily relax perf_event_paranoid). The strace above already localizes it to the env-discovery glob scan, but a flamegraph would pin the exact function.

Workaround

Disable / uninstall the ms-python.vscode-python-envs extension, or avoid opening the large workspace, or narrow python.envs.environmentDirectories so the glob doesn't span the whole tree.

Related

Metadata

Metadata

Labels

area-environmentEnvironment, interpreter related issues.bugIssue identified by VS Code Team member as probable bug

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions