Repository navigation
PSNativeCommandErrorActionPreference non-zero 0 exit code exception false positive #20034
Copy link
Copy link
Closed
Closed
Copy link
Labels
Resolution-DeclinedThe proposed feature is declined.The proposed feature is declined.WG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Description
Prerequisites
- Write a descriptive title.
- Make sure you are able to repro it on the latest released version
- Search the existing issues.
- Refer to the FAQ.
- Refer to Differences between Windows PowerShell 5.1 and PowerShell.
Steps to reproduce
There are quite a few programs that return non-zero success exit codes. With the PSNativeCommandErrorActionPreference experimental feature turned on this causes errors to be emitted to the console.
I propose that a variable and/or cmdlet maintain a dictionary of program successful exit codes to be checked at runtime when a program runs a non-zero exit code. The dictionary could also be expanded to include error codes. Imagine a community module(s) with a set of programs and success/error exit codes with descriptions that PowerShell could leverage to return to the user.
Register-ProgramExitCode [-Name] <string> [-ExitCode] <int> [[-Description] <string>] [[-Status] {Success | Error}] [<CommonParameters>]
A current example with brew:
PS /home/thomas> brew
Example usage:
brew search TEXT|/REGEX/
brew info [FORMULA|CASK...]
brew install FORMULA|CASK...
brew update
brew upgrade [FORMULA|CASK...]
brew uninstall FORMULA|CASK...
brew list [FORMULA|CASK...]
Troubleshooting:
brew config
brew doctor
brew install --verbose --debug FORMULA|CASK
Contributing:
brew create URL [--no-fetch]
brew edit [FORMULA|CASK...]
Further help:
brew commands
brew help [COMMAND]
man brew
https://docs.brew.sh
NativeCommandExitException: Program "brew" ended with non-zero exit code: 1.Expected behavior
Programs that return successful non-zero exit codes; PowerShell does not write errors.Actual behavior
Errors are written on all non-zero exit codes.Error details
Exception :
Type : System.Management.Automation.NativeCommandExitException
Path : /home/linuxbrew/.linuxbrew/bin/brew
ExitCode : 1
ProcessId : 1125
ErrorRecord :
Exception :
Type : System.Management.Automation.ParentContainsErrorRecordException
Message : Program "brew" ended with non-zero exit code: 1.
HResult : -2146233087
CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
FullyQualifiedErrorId : ProgramExitedWithNonZeroCode
Message : Program "brew" ended with non-zero exit code: 1.
HResult : -2146233087
TargetObject : /home/linuxbrew/.linuxbrew/bin/brew
CategoryInfo : NotSpecified: (/home/linuxbrew/.linuxbrew/bin/brew:String) [], NativeCommandExitException
FullyQualifiedErrorId : ProgramExitedWithNonZeroCodeEnvironment data
7.4-preview4Visuals
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Resolution-DeclinedThe proposed feature is declined.The proposed feature is declined.WG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Activity
mklement0 commentedon Jul 28, 2023
As a (largely inconsequential) aside:
brewwithout arguments does result in an error condition:brewconsiders this invalid syntax, which it signals with exit code1and - as a courtesy - outputs its CLI help to stderr.brew -horbrew --helpare the expected ways to invoke help (and result in exit code0).In the examples below, I'll use
test "$(Get-Item nosuch*)"as a (contrived) example instead; the Unixtestutility by design sets its exit code to1if the test evaluates to false (which isn't a failure condition).A more realistic example is
robocopy.exe, which uses several nonzero exit codes for success conditions - see the discussion in #18944.An alternative per-call solution - not mutually exclusive with your proposal - is part of the larger
Invoke-NativeCommandproposal in #18991 (it also proposes a per-call solution for overriding$PSNativeCommandArgumentPassing); it currently proposes something likeInvoke-NativeCommand -IgnoreExitCode { test "$(Get-Item nosuch*)" }; if ($LASTEXITCODE) { ... }That is, the assumption is that the caller will inspect the exit code later and take appropriate action.
Conceivably, this could be augmented as follows, in cases where the caller only cares about success vs. failure, not the specific exit code (which with
testobviously doesn't make sense):Invoke-NativeCommand -SuccessExitCode 0, 1 { test "$(Get-Item nosuch*)" }; ...My personal concerns with respect to a potential
Register-ProgramExitCodecmdlet are twofold:While there definitely are high-profile CLIs - notably
robocopy- that call for a solution that doesn't conflict with$PSNativeCommandUseErrorActionPreference, my sense is that they are a small percentage overall - most utilities play by the rules (0for success, nonzero for failure), so a registry of such exit codes may not be worth the effort.On the flip side, if maintained at an organization level (rather than via a community module, see next point), I can see the appeal of a set-it-and-forget-it solution (but, as with any preference-variable-like solution, it amounts to invisible state, so that looking at a call in isolation you won't know how its exit code will be handled).
While a community effort always has its appeal, entrusting something as critical as error handling to a community-maintained module sounds problematic to me; for instance, users can reasonably argue over which of
robocopy's nonzero exit codes should be considered a failure, utilities as a whole may be missing, values may be missing, the registered values can go stale over time, ...ThomasNieto commentedon Jul 29, 2023
I agree that the example provided was not the best and robocopy would have been a better one.
Lets first level set on some common points/facts.
$PSNativeCommandUseErrorActionPreferenceset to$trueby default is a breaking change due to a new unhandled exception being thrown. Looking at the RFC it should be disabled by default but might just be enabled for preview?Now for my views on this feature:
Invoke-NativeCommandRegister-ProgramExitCodefeature was implemented, PowerShell should not ship/enable a default registry of exit codes. That should be up to the user on what they want configured. If a user, community, or official PowerShell module(s) was created with a registry, it shouldn't be a concern since the user would make the active choice to install and import the module with the ability to overwrite any registration. I would equate this toRegister-ArgumentCompleter, the command exists for users to add their own completers and community modules likeTabExpansionPlusPlusare available. The user is free to install a module with completers and overwrite them as desired.test.execonnects to a web service, exit code 5 is invalid credential. The user creates a registrationRegister-ProgramExitCode -Name test.exe -ExitCode 5 -Description 'Invalid credential'. Now when the user runs the program they get an error with the specific message instead of a generic non-zero exit code exception with a number they have to look up for the meaning.mklement0 commentedon Jul 29, 2023
Fortunately that won't be the case: As Steve Lee (@SteveL-MSFT) said here:
In other words: the feature will be off by default in the release version - sensibly so.
It will be opt-in, just like Bash's
set -eis.mklement0 commentedon Jul 29, 2023
Glad to hear it; that addresses my second concern.
I think proper framing is important here:
0-as-success-nonzero-as-failure is only a convention, yes, but a widely observed one with a long history.&&and||in POSIX-compatible shells,cmd.exe, and nowadays also in PowerShellset -ein Bash, for instance, and soon$PSNativeCommandUseErrorActionPreferencein PowerShellSo it should be noted that straying from this convention is ill-advised, because it runs afoul of core shell features, especially in the Unix world.
mklement0 commentedon Jul 29, 2023
P.S.: To be clear:
robocopydoes. And, in the case of error conditions, a corresponding error message is usually emitted by the program itself, to stderr.SteveL-MSFT commentedon Oct 9, 2023
Although
PSNativeCommandUseErrorActionPreferenceis to be stable in 7.4, it does default to$falseto preserve backwards compatibility. Forrobocopytype situations, you would need to write that to disable and re-enable this feature if you intend to use this feature in your script. There is still debate in the WG if having a user settable dictionary is desirable for the longterm.rkeithhill commentedon Nov 28, 2023
The @PowerShell/wg-powershell-engine discussed this and concluded that it's not worth adding a mechanism to register successful non-zero exit codes. As Steve Lee (@SteveL-MSFT) points out, this feature is not enabled by default. It is "opt-in". And there is a documented mechanism to handle these exception cases in scripts where you have enabled
$PSNativeCommandUseErrorActionPreference. From theabout_Preference_Variablesdocs:Although I suggest we update the docs to take advantage of scriptblock scoping & cleanup e.g.:
microsoft-github-policy-service commentedon Dec 2, 2023
This issue has been marked as declined and has not had any activity for 1 day. It has been closed for housekeeping purposes.