deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

WMIC's removal in Windows 11 silently breaks Node process-tree handling in pidtree

Windows 11 no longer ships wmic, so pidtree 0.6.0 fails on every call — and the failure surfaces as an empty process tree, not an error. Version 1.0.0 has a PowerShell fallback, but ^0.6.0 ranges never resolve to it.

WMIC's removal in Windows 11 silently breaks Node process-tree handling in pidtree

What broke

Microsoft's removal of the WMIC command-line tool from Windows 11 is quietly breaking process-tree detection in Node tooling. According to a post on dev.to, the npm package pidtree — described there as the standard way to enumerate a process tree from Node, backing pkill-style behaviour in a lot of tooling and drawing millions of downloads a week — shells out to wmic on Windows in every version below 1.0.0. On a current Windows 11 install the binary no longer exists, so each call rejects with a "spawn wmic ENOENT" error. Worse, the rejection tends to get swallowed: the author found the error landing inside a Promise.allSettled whose caller interpreted a failed lookup as "this process has no children".

The visible symptom came first. A terminal pane hosting a coding agent reported about 77 MB of memory — roughly the PowerShell host alone, with neither the agent nor the dev server it had started counted. Every pane read as an idle shell.

A documented removal, an invisible failure

Microsoft deprecated WMIC in Windows 10 21H1 and began stripping it from the default image with Windows 11 22H2, the post notes. On the author's test machine — Windows 11 Home, build 26200, checked in August 2026 — Get-Command wmic simply returns not recognized. The removal was announced and documented; the breakage persists anyway, because wmic invocations live inside libraries rather than in the application code developers actually wrote.

pidtree 1.0.0 addresses this: it still attempts wmic first, then falls back to PowerShell's Get-CimInstance when the spawn fails. The catch is semver. A caret range of ^0.6.0 will never resolve to 1.0.0, because caret semantics on 0.x releases only move within the same minor. The author's own project declared ^0.6.0, the lockfile pinned 0.6.0, and every Windows call consequently returned a tree containing just the queried process. The practical advice: check the version you actually resolve, not the one package. implies.

Replacing wmic yourself

For those who cannot upgrade, the post recommends Get-CimInstance Win32_Process — with a performance caveat. The instinct is to issue one filtered query per PID, but the author's benchmark shows a single snapshot of all 396 processes completing in about 200 ms while five filtered queries took 517 ms. The overhead sits in initiating each CIM query, not in the volume of rows returned. The recommended pattern is one snapshot per polling cycle, converted into an in-memory parent-to-children map that you then walk for each PID you care about.

Killing a tree on Windows

The post also covers the second half of the problem: termination. Windows has no POSIX-style process groups and no automatic cleanup of orphans, so Stop-Process and Node's process.kill(pid) each end exactly one process. Killing a root shell leaves its descendants running, still holding open files and ports, with parent PIDs that now reference a dead process.

taskkill /F /T handles this by walking the tree and terminating descendants before their ancestors. The ordering matters: kill the root first and the parent links you needed to locate the orphans are destroyed, leaving unattached processes that nothing connects back to the thing you were cleaning up. Resolve the tree first, or delegate the whole job to taskkill /F /T. The author also flags PID reuse as a hazard — Windows recycles PIDs aggressively, so any PID cached across polling cycles should be validated against a fresh snapshot before a force kill.

When the ancestry is gone entirely

Some processes cannot be reattached to their origin at all. A dev server started in the background by an agent often has no usable ancestry: intermediate processes have exited, parent PIDs are stale or recycled, and the executable is just node.exe. The one remaining signal is the process's current working directory — but Node exposes no API for reading another process's cwd on Windows. The post sketches a workaround using the koffi FFI library to call ntdll's NtQueryInformationProcess and walk the PEB to the current-directory field, while acknowledging the required offsets are undocumented and subject to change. Elevated processes and other users' processes refuse the necessary access, so the technique silently skips services and tools like Docker Desktop; denials should be logged once per PID rather than on every poll.

Why it matters

This is a concrete case of a routine, well-documented platform deprecation turning into silent breakage deep in the dependency graph. Nobody who depends on pidtree wrote a wmic call themselves; the failure arrives transitively and manifests as a plausible empty result rather than a crash, which is exactly the kind of bug that survives for years. For anyone building terminals, agents or process managers on Windows the checklist is short: verify the resolved pidtree version, take one CIM snapshot per cycle instead of per-PID queries, kill trees child-first or hand the root to taskkill /T, and never trust a cached PID across polls.

  • #node-js
  • #windows
  • #npm
  • #process-management
  • #deprecation