Repository navigation
Description
Describe the bug
On Windows, when a powershell tool command runs a script that spawns a long-lived background process using Start-Process ... -RedirectStandardOutput <file> -RedirectStandardError <file> (especially when the outer command is piped, e.g. pwsh -NoProfile -File start-server.ps1 2>&1 | Tee-Object -FilePath run.log), the child script finishes and exits normally, but the outer pwsh.exe session managed by the powershell tool stays in status: running indefinitely and prevents the session from becoming idle.
Furthermore, if stop_powershell is called on that shellId to clean up the hung outer pwsh.exe wrapper, stop_powershell terminates the entire process tree—including the newly spawned background service that was intended to stay alive.
Root cause:
- On Windows/.NET,
Start-Processwith-RedirectStandardOutput/-RedirectStandardErrorinvokesCreateProcessW(..., bInheritHandles: TRUE)without a handle allowlist (PROC_THREAD_ATTRIBUTE_HANDLE_LIST). - Any inheritable pipe write handles in the parent
pwsh.exe(such as the stdout/stderr pipe created by thepowershelltool runner or an outer| Tee-Objectpipeline) are inherited by the long-lived grandchild process. - Even after the direct child script exits, the surviving grandchild process keeps the inherited pipe write handle open, so the outer stream reader /
Tee-Objectnever receives EOF. - Because the
powershelltool waits for stream EOF andstop_powershellkills the entire process tree, there is no built-in way to finalize the completed shell wrapper without killing the background process it started.
Affected version
1.0.102 (also reproducible on 1.0.107)
Steps to reproduce the behavior
- On Windows, run a
powershelltool command insyncmode that invokes a child PowerShell script throughTee-Object:pwsh -NoProfile -File .\start-service.ps1 2>&1 | Tee-Object -FilePath .\start.log
- Inside
start-service.ps1, spawn a long-lived background process with redirected log files and then exit:Start-Process -FilePath "node.exe" -ArgumentList "-e `"setInterval(() => {}, 1000)`"" ` -WindowStyle Hidden -RedirectStandardOutput ".\service.out.log" -RedirectStandardError ".\service.err.log" | Out-Null Write-Output "Service started"
- Observe that
"Service started"is printed and the innerpwshscript process exits immediately, butlist_powershellcontinues to show theshellIdinstatus: runningindefinitely afterinitial_waitexpires. - Calling
stop_powershellon thatshellIdterminates the backgroundnode.exeprocess as well.
Expected behavior
- Once the direct command/script process exits, the
powershelltool should not remain blocked indefinitely waiting on stdout/stderr pipe handles inherited by detached/background grandchild processes. - Alternatively,
stop_powershell(or the shell runner) should provide a way to close/reap the finished outer shell wrapper without terminating intended background grandchild processes.
Additional context
- Operating system: Windows 11 (x64)
- CPU architecture: x86_64
- Shell: PowerShell 7 (
pwsh.exe)
Activity