PowerShell: Start-Process does not populate the process object's exit code with NoNewWindow switch

Prerequisites

Steps to reproduce

I noticed that the Start-Process cmdlet sometimes does not populate the ExitCode property in the output, I believe it should. This happed at least when -PassThru, -Wait, and -NoNewWindow switches are provided. -NoNewWindow seems to be the reason, since the exit code is available without it.

Steps to reproduce

You can see this behavior with this simple test script

Set-Content -Value "exit 1" -Path script.ps1
$p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
$p.ExitCode  # Should output: 1
$p = start-process "pwsh" script.ps1 -PassThru -Wait
$p.ExitCode # Outputs: 1

Expected behavior

PS C:\> set-content -Value "exit 1" -Path script.ps1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
PS C:\> $p.ExitCode
1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -Wait
PS C:\> $p.ExitCode
1

Actual behavior

PS C:\> set-content -Value "exit 1" -Path script.ps1
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait
PS C:\> $p.ExitCode
PS C:\> $p = start-process "pwsh" script.ps1 -PassThru -Wait
PS C:\> $p.ExitCode
1

Error details

No response

Environment data

PS C:\> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.0-preview.5
PSEdition                      Core
GitCommitId                    7.4.0-preview.5
OS                             Microsoft Windows 10.0.20348
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

About this issue

  • Original URL
  • State: open
  • Created 9 months ago
  • Reactions: 11
  • Comments: 19 (5 by maintainers)

Most upvoted comments

I am also experiencing this issue now after upgrading to PowerShell v7.4.0 x64 on Windows.

In PowerShell v7.3.10 it works fine: image

In PowerShell v7.4.0 it doesn’t return the ExitCode with -NoNewWindow, while it does return the ExitCode without -NoNewWindow: image

It looks like this will be fixed in v7.4.1:

Just to add … this doesn’t happen on MacOS …

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Darwin 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Just to be consistent …

PS /Users/softwarebear/dev> (Start-Process -Wait -NoNewWindow -PassThru whoami).ExitCode
softwarebear                    
0
PS /Users/softwarebear/dev> (Start-Process -Wait -PassThru whoami).ExitCode                
softwarebear                    
0
PS /Users/softwarebear/dev> 

Just to add … this doesn’t happen on MacOS …

Name                           Value
----                           -----
PSVersion                      7.4.0
PSEdition                      Core
GitCommitId                    7.4.0
OS                             Darwin 23.1.0 Darwin Kernel Version 23.1.0: Mon Oct  9 21:27:24 PDT 2023; root:xnu-10002.41.9~6/RELEASE_ARM64_T6000
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Although the symptoms have changed a bit, the root cause is likely the same as in:

That is, it seems that an exception occurs behind the scenes due to the process handle not being available (for reasons unknown to me), which PowerShell quietly swallows, causing $null to be returned from .ExitCode (and .Handle).

This now seems to always happen with -NoNewWindow, whereas it previously didn’t happen if -Wait was specified too.

That is, both ($p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow -Wait).Handle and $p = start-process "pwsh" script.ps1 -PassThru -NoNewWindow; $p.WaitForExit(); $p.Handle now output nothing, and therefore also return $null via .ExitCode.

In Windows PowerShell (and seemingly also earlier versions of PS Core), the -Wait variant (only) does work.

The problem does NOT occur when System.Diagnostics.Process is used directly, so this can serve as a workaround

# NOTE: You may also have to set the working dir. to PowerShell's.
$p = [System.Diagnostics.Process]::Start('pwsh', (Convert-Path 'script.ps1'))
$p.WaitForExit()
"Exit code: [$($p.ExitCode)]" # OK

Update: @derkveenhof found a simpler workaround: cache the process handle:

$p = Start-Process -NoNewWindow -PassThru pwsh '-c whoami'
$dummy = $p.Handle # Cache the handle
$p.WaitForExit()
"Exit code: [$($p.ExitCode)]" # OK

However, it should be noted that these workarounds aren’t fully equivalent, though it will probably often not make a difference: -Wait waits for the child process as well as any of ITS child processes, which .WaiForExit() and Wait-Process do not. See: