Repository navigation
S.M.A.PowerShell.HadErrors and $? return false positives when errors are suppressed #4613
Description
Activity
The whole terminating vs non-terminating errors consistently is misunderstood. Even in some of our product code I see these mistakes and I make them myself. Perhaps an option here is to add HadTerminatingErrors, while HadErrors continues to capture Terminating/Nonterminating (even if handled/suppressed)?
- addedWG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtimeIssue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
on Aug 31, 2017 KirkMunro commented
on Aug 31, 2017 ContributorAuthorMore actionsI agree discussion is required because error handling in PowerShell is a point of confusion. This particular issue has nothing to do with terminating vs non-terminating though. This has to do with handled vs non-handled, which is a completely different thing.
If I invoke a .NET method that internally receives some exception but handles that exception gracefully and then eventually returns back to me without error, should $? return $true or $false? The answer in that case is quite clear: it should return $true because there was no error as far as I, the method invoker, am concerned.
I believe PowerShell command invocations should behave no differently. In both examples I shared above, I am instructing PowerShell to suppress the error if one occurs. In other words, the error is handled in my command. Yet S.M.A.PowerShell.HadErrors returns
$true and $ ? returns $false.Aside -- This related behaviour also surprises me. Try executing these commands one at a time.
Get-Service -Name Invalid $? # returns $false Get-Service -Name Invalid -ErrorAction Stop $? # returns $false try {Get-Service -Name Invalid -ErrorAction Stop} catch {<# Ignore the error, it's benign #>} $? # returns $false -- shouldn't this return $true? & {try {Get-Service -Name Invalid -ErrorAction Stop} catch {<# Ignore the error, it's benign #>}} $? # returns $true
- addedReview - CommitteeThe PR/Issue needs a review from the PowerShell CommitteeThe PR/Issue needs a review from the PowerShell Committee
on Aug 31, 2017 I see. I think that
HadErrorsand$?should be consistent in that they are only true if there are unhandled errors, but will defer to @PowerShell/powershell-committee to understand the history and original intent.- addedCommittee-ReviewedPS-Committee has reviewed this and made a decisionPS-Committee has reviewed this and made a decisionBreaking-Changebreaking change that may affect usersbreaking change that may affect usersand removedReview - CommitteeThe PR/Issue needs a review from the PowerShell CommitteeThe PR/Issue needs a review from the PowerShell Committee
on Sep 6, 2017 @PowerShell/powershell-committee reviewed this
(get-service -name invalid); $? True get-service -name invalid; $? False
the two above statements should be equivalent, however, it's not. The bug seems to be that in Kirk Munro (@KirkMunro) 's samples above, #3 should return $true and the two equivalent statements here should both return $false.
Reacted by Kirk Munro and Joel Bennettmklement0 commented
on Sep 12, 2017 ContributorMore actionsThe reason that
(get-service -name invalid); $?returns true is the previously discussed issue that(...)turns a command into an expression and it is then (unexpectedly) the expression's success that determines the value of$?, and the mere act of enclosing a command in(...)is a "successful" expression - see Automatic $? variable is reset to $True when a command is enclosed in parentheses - make $? only reflect command status, not expression status
Re
try {Get-Service -Name Invalid -ErrorAction Stop} catch {<# Ignore the error, it's benign #>}returning$False:To recap from #3768 (comment):
checking
$?aftertry/catchis virtually pointless, because what$?is set to depends on whether the catch block happens to be empty ($False) or not (whatever statement happens to execute last in the catch block determines the value of$?).Similarly (unexpectedly), anything that you invoke via
& { ... }- i.e.,&with a script block - currently makes$?indicate$True; try& { nosuchcommand }; $?and& { 1 / 0 }; $?- addedDocumentation Needed in this repoDocumentation is needed in this repoDocumentation is needed in this repo
on Mar 16, 2018 Re-reviewing issues @PowerShell/powershell-committee had reviewed prior to GA to see if the decision would still stand post GA.
For this issue, based on Michael Klement (@mklement0)'s explanation, it seems this is working as designed and I think just needs to be documented appropriately.
12 remaining items
Someone just hit the
$?-is-still-$false-with--ErrorAction Ignoreissue indirectly on Stack Overflow, in the context of a Makefile:A recipe such as
powershell -c " Remove-Item -ErrorAction Ignore file.txt"unexpectedly fails if an error was ignored, because$?is mapped onto exit codes0and1.vexx32 commented
on Apr 3, 2019 CollaboratorMore actionsI think perhaps this might be resolved by splitting the functionality expected from
$?Currently it reports a mixed status, depending on the last executed command OR expression. And, as far as I can think this morning, the only way an expression would fail is if it's pretty much nonsensical and is either a parse or something like a math error, using operators in a context they don't allow, e.g.,
"hello" % 2What if we split this in two, so that we have a variable like
$LastCmdletFailedand a$LastExpressionFailed?Agreed that
$?shouldn't report virtually-always-successful expression status, Rain Sallow (/u/ta11ow) (@vexx32), and this has come up before in #3359, which asks that$?:- only ever reflect the last command's status
- including whether the previous statement (whether command or expression) was aborted due to a statement-terminating error - which is what
"hello" % 2triggers (more simply1 / 0).
In other words: if an expression (or command) was aborted due to a statement-terminating error,
$?is sensibly$false, but it never makes sense for a "successful" (i.e., non-aborted) expression to set$?to$true, as is currently the case.With that, I'd say there's no need for a separate
$LastExpressionFailed.Reacted by Rain Sallow (/u/ta11ow)vexx32 commented
on Apr 3, 2019 CollaboratorMore actionsThat certainly seems to be pretty sensible to me. I can't really think of a case that doesn't effectively cover. 😄
Reacted by Michael KlementKirkMunro commented
on Apr 10, 2019 ContributorAuthorMore actionsonly ever reflect the last command's status
I'd amend the quoted statement, as follows:
"only ever reflect the status of the last command that was executed in the current scope".
I've never, ever wanted it to work differently, and could care less about the status of the last command that was invoked any number of levels deep inside of something I invoked.
Reacted by Michael Klement and Rain Sallow (/u/ta11ow)vexx32 commented
on Apr 10, 2019 CollaboratorMore actionsDefinitely agreed on that. If my function handles an error state but doesn't itself emit an error, that error should not magically show up in
$error or $ ?.mklement0 commented
on Apr 10, 2019 ContributorMore actionsAgreed.
- Fortunately, at least in PowerShell code it already seems to work that way:
& { gci /nosuch }; $? # $True - fortunately, the gci status did NOT leak
- Unfortunately, you can't set the
$?status for your caller from PowerShell code - see Write-Error in a function doesn't set automatic success variable $? to $False (doesn't set the execution status to indicate a non-terminating error) in the caller's scope #3629
& { Write-Error 'oh no' }; $? # !! $True - despite the use of Write-Error
The workaround is to use
$PSCmdlet.WriteError(), but that's (a) cumbersome and (b) only available in advanced function/scripts.Reacted by Rain Sallow (/u/ta11ow) and Kirk Munromicrosoft-github-policy-service commented
on Nov 16, 2023 ContributorMore actionsThis issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented
on Nov 16, 2023 ContributorMore actionsThis issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
- addedResolution-No ActivityIssue has had no activity for 6 months or moreIssue has had no activity for 6 months or more
on Nov 16, 2023 microsoft-github-policy-service commented
on Nov 16, 2023 ContributorMore actionsThis issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented
on Nov 23, 2023 ContributorMore actionsThis issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.
Created from discussion started on #3768.
Description
If you invoke PowerShell using the System.Management.Automation.PowerShell class, and if you handle the errors in the command you invoke by indicating that they should be ignored, S.M.A.PowerShell will still set HadErrors to true. Below are some code samples illustrating the problem.
Sample 1: C#/.NET invocation of PowerShell commands/scripts
Sample 1: Expected behavior
$ps.HadErrors should return $false
Sample 1: Actual behavior
$ps.HadErrors returns $true, even when $ps.Streams.Error does not contain any errors (because there were no errors!)
Sample 2: Direct command invocation in PowerShell
Sample 2: Expected behavior
$? should return $true
Sample 2: Actual behavior
$? returns $false, even though there were no errors because the invoker of the command instructed PowerShell to ignore the error that would otherwise have been raised because it is a benign error for them (and therefore, not an error)
Additional Details
In both of these cases, -ErrorAction Ignore is being used to tell PowerShell that the error is actually not an error as far as this invocation is concerned, and therefore it can be completely ignored. Why then, do both $? and$ps.HadErrors indicate there was still error? Technically there was an error, by definition of the command being invoked, but it is being handled/treated like it is not an error in the command from which the command producing the error was invoked. In both of these cases $ ? and $ps.HadErrors should have values indicating that there wasn't an error. If you do not trust the command you are invoking to ignore benign errors and only notify you about errors that you actually need to care about then you should not be invoking that command.
Environment data