question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

PowerShell CLI: exits with exit code 1 rather than the specific $LASTEXITCODE of the last statement

See original GitHub issue

Follow-up from #11461.

When using the PowerShell CLI with -c / -Command, PowerShell by design sets its exit code based on the success status of the last statement executed in the specified command string.

If the last statement is a PowerShell command, success can only be derived from the automatic success-status variable, $?. As a Boolean, the only sensibly way to map that to an exit code is to map $true to 0, and $false to 1 - this works as expected.

However, if the last statement is a call to an external program, a process exit code is directly available from the child process in which the external program executed, as reflected in automatic variable $LASTEXITCODE. The same potentially applies to a call to an (in-process) call to a *.ps1 script that uses exit <n>.

Given that $LASTEXITCODE can contain more specific information than just abstract success vs. failure, it makes sense to preserve this specific information in the caller’s $LASTEXITCODE value instead of mapping it in a lossy manner to either 0 ($LASTEXITCODE being 0) or 1 (any nonzero $LASTEXITCODE value) - which is what currently happens.

While this would technically be a breaking change, it seems to me that it falls into Bucket 3: Unlikely Grey Area:

  • In the typical case, code checks for abstract success vs. failure of child processes, which means that any nonzero exit code indicates failure, without regard to the specific nonzero value.

  • The proposed change wouldn’t interfere with that, while at the same time providing more specific information to code that does care about the specific nonzero exit code reported.

Steps to reproduce

On Unix:

& { pwsh -noprofile -c 'sh -c ''exit 5'''; $LASTEXITCODE } | Should -Be 5

Expected behavior

The test should pass.

Actual behavior

The test fails, because the process exit code 5 was unexpectedly mapped to 1 in the caller’s scope:

Expected 5, but got 1.

Environment data

PowerShell Core 7.1.0-preview.6

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

6reactions
mklement0commented, Aug 26, 2020

it’s established behaviour

Unfortunately, it’s not documented, though, so let’s start there:

In the process I discovered:

  • <kbd>Ctrl-C</kbd> with -File sets the exit code to 0(!) - see #13523

  • Somewhat ironically, in-session termination of a script with <kbd>Ctrl-C</kbd> sets $LASTEXITCODE to the POSIX-compliant 130, but only on Unix; on Windows, $LASTEXITCODE isn’t set at all in that case (same as not exiting with exit from the script - see #11712)

  • A non-numeric exit argument or a numeric value outside the platform-supported range ([int] on Windows, [byte] on Unix) quietly results in 0(!).

3reactions
mklement0commented, Aug 22, 2020

@Northman-de

I do not believe that it’s PowerShell’s task to handle the errorcode of another executable

It is PowerShell’s job - as a shell - to play as nicely with the outside world as it can - an aspiration that it has historically often fallen short of.

With -c / -Command, PowerShell’s CLI sets an exit code deliberately - which is good - but it throws away information in the process - which is bad.

Clearly, the design intent was to translate the success status of the (last) command executed in the command string into an appropriate process exit code; for PowerShell-native commands mapping $? to 0 and 1 is the best that can be done, but for external executables and *.ps1 scripts an appropriate exit code is directly available and should be used as such.

Note that calling a *.ps1 script with -File does respect an exit <n> statement and reports <n> as the exit code:

PS> 'exit 5' > ./t.ps1; pwsh -noprofile -file ./t.ps1; $LASTEXITCODE
5

There are situations where you need to call .ps1 scripts via -c / -Command instead, such as when you need to pass array arguments.

PS> 'exit 5' > ./t.ps1; pwsh -noprofile -c ./t.ps1 -param foo, bar; $LASTEXITCODE
1  # !! Specific exit code was lost, solely due to switching from -File to -Command.

Does this discrepancy make sense to you? I think it amounts to a pitfall that is easily avoided.


Especially if the exitcode would be ambiguous.

This is really a separate issue, worth tackling in its own right. It is an issue that (a) already exists and (b) one that, if anything, could be helped by resolving the issue at hand, making workarounds easier.

It is unfortunate that PowerShell chose to use the nondescript exit code 1 for termination via <kbd>Ctrl-C</kbd> and for unhandled script-terminating errors (created with throw, for instance).

POSIX-like shells more sensibly reserve a range of exit codes to indicate termination by signal (which <kbd>Ctrl-C</kbd> constitutes, via SIGINT): 128 + <signal-number; thus, since SIGINT has a numeric value of 2, exit code 130 is reported.

Read more comments on GitHub >

github_iconTop Results From Across the Web

PowerShell and process exit codes - error handling
Arguably, -Command ( -c ) should report the specific exit code of the last statement - provided it has one - instead of...
Read more >
Get Exit Code of Last Command in PowerShell [ 2 Ways]
The exit command terminates the current PowerShell script or session in PowerShell. ... Write-Host "An error occurred, exiting with code 1".
Read more >
about PowerShell exe
The process exit code is determined by status of the last (executed) command within the script block. The exit code is 0 when...
Read more >
How to Use the PowerShell Exit Command and Friends
When PowerShell runs the last command in a script, it stores the exit code of that last command in the $LASTEXITCODE variable.
Read more >
Steps to return error codes on Powershell scripts
$LASTEXITCODE holds the last error code in the powershell script. It is in form of boolean values, with 0 for success and 1...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found