Skip to content

[Robustness] Servy.psm1 Invoke-ServyCli — finally block calls process.WaitForExit() with no timeout; if Kill() failed, PowerShell hangs forever #879

@Christophe-Rogiers

Description

@Christophe-Rogiers

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.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions