Bug: Spawned child processes on Windows are missing standard environment variables (windir, SESSIONNAME, COMPUTERNAME, USERDOMAIN)
Environment: Windows 10/11, Desktop Commander (DXT extension) running inside Claude Desktop'''s Electron/Node utility process. Reproduced on two independent machines (different Windows versions, different hardware).
Symptom
Any child process started via start_process runs with a Windows environment block that is missing several variables Windows normally sets for every interactive process, including at minimum:
- windir
- SESSIONNAME
- COMPUTERNAME
- USERDOMAIN
SystemRoot is present and correct; windir is not (they'''re normally near-duplicates).
Impact
This breaks any Windows API call that relies on the OS auto-expanding %windir% (or other missing variables) inside a REG_EXPAND_SZ registry value. Concretely reproduced with SAPI/Speech_OneCore text-to-speech:
- SAPI.SpVoice.Speak() fails with SPERR_NOT_FOUND (HRESULT 0x8004503A) for every installed voice, every audio output device, in both 32-bit and 64-bit child processes.
- Process Monitor trace shows the root cause directly:
CreateFile C:\WINDOWS\system32\%windir%\Speech_OneCore\Engines\TTS\de-DE\MSTTSLocdeDE.dat PATH NOT FOUND
The registry value read by the engine contains a literal %windir%\... path; since windir is absent from the child process'''s environment, Windows leaves the placeholder unexpanded, producing a broken, non-existent path.
- Confirmed fix: explicitly setting os.environ['''windir'''] (Python) before instantiating the SAPI COM object resolves the issue completely -- Speak() succeeds immediately after.
- Confirmed via a clean control test: the identical script launched through Windows Task Scheduler instead of through start_process works without any patch, in the same user session, same desktop, same Window Station.
Other Windows APIs that read %windir%/%SESSIONNAME%/etc. from the environment (rather than resolving them via a Win32 API call) would presumably be affected the same way.
What'''s already been ruled out
Not caused by: Window Station/Desktop assignment (both normal, WinSta0/Default), AppContainer (false), Job Object UI restrictions (present but empty, 0x0), integrity level (Medium, standard), DCOM local-activation permission denial (no corresponding Event Viewer DistributedCOM 10016 event at the time of failure), or process bitness (fails identically in 32-bit and 64-bit children).
Suggested fix
When constructing the environment for spawned child processes (likely in the spawn()/child_process call in terminal-manager.js), either pass through the full parent environment (...process.env) unmodified, or explicitly ensure at least windir, SESSIONNAME, COMPUTERNAME, and USERDOMAIN are present, falling back to SystemRoot / computed values if actually absent from the parent.
Reproduction (minimal)
import os
print(repr(os.environ.get('''windir'''))) # None in a DC-spawned process, C:\\Windows in a normally-launched one
import win32com.client
spk = win32com.client.Dispatch('''SAPI.SpVoice''')
spk.Speak('''test''') # raises com_error 0x8004503A in a DC-spawned process
Bug: Spawned child processes on Windows are missing standard environment variables (windir, SESSIONNAME, COMPUTERNAME, USERDOMAIN)
Environment: Windows 10/11, Desktop Commander (DXT extension) running inside Claude Desktop'''s Electron/Node utility process. Reproduced on two independent machines (different Windows versions, different hardware).
Symptom
Any child process started via start_process runs with a Windows environment block that is missing several variables Windows normally sets for every interactive process, including at minimum:
SystemRoot is present and correct; windir is not (they'''re normally near-duplicates).
Impact
This breaks any Windows API call that relies on the OS auto-expanding %windir% (or other missing variables) inside a REG_EXPAND_SZ registry value. Concretely reproduced with SAPI/Speech_OneCore text-to-speech:
CreateFile C:\WINDOWS\system32\%windir%\Speech_OneCore\Engines\TTS\de-DE\MSTTSLocdeDE.dat PATH NOT FOUND
The registry value read by the engine contains a literal %windir%\... path; since windir is absent from the child process'''s environment, Windows leaves the placeholder unexpanded, producing a broken, non-existent path.
Other Windows APIs that read %windir%/%SESSIONNAME%/etc. from the environment (rather than resolving them via a Win32 API call) would presumably be affected the same way.
What'''s already been ruled out
Not caused by: Window Station/Desktop assignment (both normal, WinSta0/Default), AppContainer (false), Job Object UI restrictions (present but empty, 0x0), integrity level (Medium, standard), DCOM local-activation permission denial (no corresponding Event Viewer DistributedCOM 10016 event at the time of failure), or process bitness (fails identically in 32-bit and 64-bit children).
Suggested fix
When constructing the environment for spawned child processes (likely in the spawn()/child_process call in terminal-manager.js), either pass through the full parent environment (...process.env) unmodified, or explicitly ensure at least windir, SESSIONNAME, COMPUTERNAME, and USERDOMAIN are present, falling back to SystemRoot / computed values if actually absent from the parent.
Reproduction (minimal)