Severity: Warning
File: src/Servy.CLI/Servy.psm1
Location: Invoke-ServyCli finally block, line ~434–438
finally {
if ($null -ne $process) {
try { $process.CancelErrorRead() } catch {}
try { $process.WaitForExit() } catch {} # let in-flight events drain
}
...
Problem
The function already handles a timeout earlier (lines ~366–386) by calling $process.Kill() and a bounded WaitForExit(5000). If the kill itself fails, the code throws with a warning that the process may still be running.
That throw enters this finally block, which then calls $process.WaitForExit() with no timeout argument. Process.WaitForExit() without a timeout blocks until the process exits and until all redirected output streams drain — there is no upper bound. The surrounding try { … } catch { } only swallows exceptions, not hangs.
Net effect on the timeout-with-failed-kill path:
- The user has just been told the operation was abandoned.
- PowerShell silently blocks at this finally line, indefinitely, holding the spinner / pipeline.
- Ctrl+C is the only way out, leaving the orphan process still alive and the module session in an unknown state.
The same hazard applies whenever any earlier exception escapes after $process.Start() succeeded but before normal exit — the finally always tries an unbounded wait.
Suggested fix
Bound the drain wait the same way the kill path does:
try { [void]$process.WaitForExit(5000) } catch {}
5 s matches the existing flush/kill budget elsewhere in the function. Streams that haven't drained in 5 s after CancelErrorRead are not going to drain.
Severity: Warning
File:
src/Servy.CLI/Servy.psm1Location:
Invoke-ServyClifinally block, line ~434–438Problem
The function already handles a timeout earlier (lines ~366–386) by calling
$process.Kill()and a boundedWaitForExit(5000). If the kill itself fails, the code throws with a warning that the process may still be running.That throw enters this
finallyblock, which then calls$process.WaitForExit()with no timeout argument.Process.WaitForExit()without a timeout blocks until the process exits and until all redirected output streams drain — there is no upper bound. The surroundingtry { … } catch { }only swallows exceptions, not hangs.Net effect on the timeout-with-failed-kill path:
The same hazard applies whenever any earlier exception escapes after
$process.Start()succeeded but before normal exit — the finally always tries an unbounded wait.Suggested fix
Bound the drain wait the same way the kill path does:
5 s matches the existing flush/kill budget elsewhere in the function. Streams that haven't drained in 5 s after CancelErrorRead are not going to drain.