-
Notifications
You must be signed in to change notification settings - Fork 7.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
$? is not set to $False even command fails #18343
Comments
The value of erroractionPreference and what happens inside the command being called will impact $?
There was an error but the command still ran to completion - this is the test for "succeeded" - setting error action prevents it running to completion.
"Did what I wanted", "Ran without raising any error", and "Succeeded" are not not the same, and the difference is a surprise sometimes. |
If I understand correctly, what sets However, for commands written in PowerShell, statement-terminating errors or That Additionally, the ability for scripts and function to set Therefore,
|
@mklement0
Using my other example
divide by zero is a terminating error, but when ErrorActionPreference is "continue" it is considered to have been handled after printing it. So the function exits normally and $? says "true" |
Quick terminology note: There are two types of terminating errors:
This terminology isn't official but it's useful for making sense of what PowerShell does, and I've used it here and on Stack Overflow. (The names could be revisited, should they became part of the official docs; speaking of: |
@jhoneill, as for your examples: Your Inside a script or function there's no point in combining (Interactively, Your What That is, whether or not you follow function foo { 1 / 0 }; foo; $? # -> $true |
@sumit-kakadiya so for your original issue, does setting |
Switching to Also, Unless there is truly a bug here, the fact hat
You mention proxy functions: do they use Is there an aspect we're not considering, such as CDXML / implicit remoting? |
@mklement0 since this is ExchangeOnline, it's using implicit remoting so proxy functions are generated locally, hence was looking for a workaround in the interim. |
Thanks, @SteveL-MSFT, so just to clarify: Auto-generated functions created in the context of implicit remoting have the same problem that local functions that use If (distant) memory serves, the only way to get implicit remoting functions to generate a terminating error is to set However, this makes errors script-terminating ones, which can therefore only be handled with |
TBH I think as a way of testing whether ANY command had an error
Requires us to think back to foo when our brains fill in the |
@jhoneill, yeah, Get-Item -NoSuchParameter # a statement-terminating error reported by the parameter binder.
$? # -> $false
Get-Item NoSuchFile || 'dang!' # with non-terminating error -> 'dang!' prints (too)
Get-Item -NoSuchParameter || 'dang!' # with statement-terminating error -> 'dang!' prints (too)
# Unfortunately, does NOT work with functions that use Write-Error, as discussed
function foo { Write-Error 'non-terminating error' }; foo || 'dang!' # !! 'dang!' does NOT print To distinguish between non-terminating and terminating errors (whether statement- or script-terminating), you need # With non-terminating error -> error prints, try / catch is IGNORED
try { Get-Item NoSuchFile } catch { 'I AM NEVER CALLED' }
# With terminating errors -> no error output, catch block is called.
try { Get-Item -NoSuchParameter } catch { 'CAUGHT!' } # statement-terminating error - 'CAUGHT!' prints
try { throw 'A fit' } catch { 'CAUGHT!' } # script-terminating error - 'CAUGHT!' prints As an aside:
|
My examples assume the default value for
Immediately after a cmdlet call that produced at least one non-terminating error (error records written to the error stream), try { Get-Item NoSuchFile } catch { 'I AM NEVER CALLED' }
$? # -> $false
Resolving this asymmetry is being considered, but it would be a substantial breaking change:
I don't understand. |
No, -ErrorAction definitely changes the behaviour for terminating errors.
And for throw
Which is why I advise people to follow throw with return.
OK. You have an old fashioned batch file. In it you have ping with invalid parameters. But the next line of the batch file runs. Even though ping terminated unless the next line is "check what happened and abort if ping failed" the rest of the script runs. So here there is no such thing as a script terminating error. An error might terminate a command, but it can't stop the batch file that called it. In a C# program you might have the equivalent of PowerShell has something different again.
Having action preference set to stop causes the function to exit without writing the verbose message, but when the message bubbles up to the command line where action is continue, it is printed and the next command runs. This is what I mean by printing the message is a treated as catching it But
This makes write error return a pipeline terminating error, we can only catch it... |
# Prints the error message and continues. [UPDATE: No quite] Omitting -ErrorAction Stop behaves the same.
Get-Tangent "hello" -verbose -ErrorAction Stop; 'after'
# As opposed to (prints "caught!" only, 'after' doesn't get to execute):
try { Get-Tangent "hello" -verbose -ErrorAction Stop; 'after' } catch { 'caught!' }
# Via $ErrorActionPreference = 'Stop' - but UNLIKE with -ErrorAction Stop - the statement-terminating
# error becomes a script-terminating one, so 'after' doesn't get to execute.
& {
$ErrorActionPreference = 'Stop'
Get-Tangent "hello" -verbose; 'after'
} |
As for the rest of your previous comment: no argument there, but it also drives home the need for specific terms for the two kinds of terminating errors that can occur in PowerShell, based on the what the unit of execution is that they terminate.
I'm calling this a statement-terminating error, because (by default) it terminates just that statement, and continues execution. By contrast, It's interesting to note that binary cmdlets cannot themselves create such errors; |
Yes, good point, I missed one aspect: An uncaught statement-terminating error (exception) inside a command that happens to be implemented as a PowerShell script or function, is susceptible to # !! 'after' still prints
& { [CmdletBinding()]param() $ErrorActionPreference = 'Stop'; 1 / 0 }; 'after'
# !! Ditto - ErrorAction Stop was NOT effective at the *command level*
& { [CmdletBinding()]param() 1 / 0 } -ErrorAction Stop; 'after'
# !! A SIMPLE function, by contrast, does create a *script*-terminating error: 'after' does NOT print.
& { param() $ErrorActionPreference = 'Stop'; 1 / 0 }; 'after' But that is an implementation detail of the command, and, strictly speaking, a statement-terminating error that happens to occur inside a function is not the same as a deliberately emitted statement-terminating error, which requires It fits into the larger theme of cmdlet-like commands implemented in PowerShell not behaving the same as binary cmdlets, as is the case with However:
In short:
|
As for
(And, yes, you can silence / ignore even script-terminating errors with |
The bottom line with respect to authoring advanced functions or scripts is:
Note that while you're still free to use Here's a sample function that demonstrates the necessary techniques: Function Get-Foo {
[CmdletBinding()]
param(
[string] $Path = '/',
[string] $NumString = '0'
)
# Relay any non-terminating errors from PowerShell-native commands via
# $PSCmdlet.WriteError(), and any terminating error (including exceptions
# from expressions / .NET method calls) via $PSCmdlet.ThrowTerminatingError()
try {
# Stderr output need not necessarily be silenced - it isn't
# affected by -ErrorAction Stop / $ErrorActionPreference = 'Stop'
& ($IsWindows ? 'cmd' : 'sh') ($IsWindows ? '/c' : '-c') 'echo stderr output >&2'
# Handle *non-terminating* errors, as happens when $Path doesn't exist.
# NOTE:
# * If you don't care about any errors, use just -ErrorAction Ignore
# * 2>$null does NOT work, as it would abort processing right away
# when invoked with -ErrorAction Stop
# Any cmdlet call that results in *statement-terminating* error would be
# handled in the `catch` block.
(Get-Item $Path -ErrorVariable errs -ErrorAction SilentlyContinue).FullName
# If errors were captured, relay them via $PSCmdlet.WriteError()
if ($errs) {
foreach ($err in $errs) { $PSCmdlet.WriteError($err) }
}
# Handle a potential terminating error.
# If $NumString can't be parsed as an integer, the
# resulting exception amounts to a statement-terminating error,
# which is handled in the `catch` block.
[int]::Parse($NumString)
}
catch {
# Relay as a statement-terminating error.
$PSCmdlet.ThrowTerminatingError($_)
# Note: To emit a *script*-terminating error instead, use:
# throw $_
}
'Done.'
} Some sample calls:
|
This 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. |
1 similar comment
This 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. |
This 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. |
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes. |
Prerequisites
Steps to reproduce
facing issue in version: Powershell version powershell-7.2 and on powershell-7.1.3
OS: centos 7
module used : ExchangeOnlineManagement
Issue: I connected o365 via powershell using application. I connected succesfully. I tried to fetch any connector exists or not.
That gets failed. But "$?" was not set to False. It always remains True. Earlier it was set to False in such a case.
Same issue I faced when I create any connector. IF connector creations fails, "$?" remains True instead of False.
In below code sample:
code sample:
$EncPassword = ConvertTo-SecureString -String 'passowd' -AsPlainText -Force
Connect-ExchangeOnline -AppId 'appid of mine' -CertificateFilePath '/home/cert.pfx' -CertificatePassword $EncPassword -Organization 'myorgdomain.onmicrosoft.com'
write-host "connected"
Get-InboundConnector 'connector1'
if ($? -eq $True)
{
write-host "inbound connector exist"
}
else
{
write-host "inbound connector does not exist"
}
try
{
New-TransportRule -Name 'myrule' -FromScope NotInOrganization -SentToScope InOrganization -Enabled $true -Priority 0 -SetSCL -1
if ($? -eq $True)
{
write-host "inbound rule created"
}
else
{
write-host "inbound rule creation failed"
}
}
catch
{ write-error "This is exception" }
Expected behavior
Actual behavior
Error details
No response
Environment data
Visuals
No response
The text was updated successfully, but these errors were encountered: