You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
How, exactly, does PowerShell decide whether .ThrowTerminatingError() terminates only the statement? #6098
outputs catch which indicates that, in this case, .ThrowTerminatingError() terminates more than just the statement.
What is happening with flow of control in the code with the try{} block? Does PowerShell search the whole call stack for a try{}catch{}? Are there circumstances aside from a wrapping try{} that results in .ThrowTerminatingError terminating more than just the statement?
would be statement after not catch. The output is catch indicating that more happened with flow of control than merely terminating the pipeline established by 1 | a.
You should view this from the perspective of the try / catch statement:
If a terminating error occurs - whether script- or statement-terminating - the entire try block is exited at the point the error occurs and execution resumes in the catch block.
(A nonterminating error, by contrast, is not caught.)
Except for the terminating-vs.-nonterminating error distinction, this is consistent with the usual try / catch semantics in, say, C#.
Just to make sure we're on the same page, the situation is at least this complicated:
Error Reported by
inside try{}
no try {}
Write-Error
continue with next statement
continue with next statement
throw
jump to catch{}
stop execution
$PSCmdlet.ThrowTerminatingError
jump to catch{}
continue with next statement
It seems to me that $PSCmdlet.ThrowTerminating() is neither consistently "non-terminating" nor consistently "terminating".
Except for the terminating-vs.-nonterminating error distinction, this is consistent with the usual try / catch semantics in, say, C#.
I'm not proficient at C#. Are there C# statements that continue to the next statement if there is no surrounding try{} block but jump to catch{} otherwise? That's what $PSCmdlet.ThrowTerminating() seems to do. I would consider PowerShell throw's behavior consistent with usual try/catch semantics, but not $PSCmdlet.ThrowTerminating().
If a terminating error occurs - whether script- or statement-terminating - the entire try block is exited at the point the error occurs and execution resumes in the catch block.
This much is evident from the repro in my OP. But it doesn't really answer these questions from my OP:
Does PowerShell search the whole call stack for a try{}catch{}? Are there circumstances aside from a wrapping try{} that results in .ThrowTerminatingError terminating more than just the statement?
So far, I don't see anything that contradicts my explanation.
It seems to me that $PSCmdlet.ThrowTerminating() is neither consistently "non-terminating" nor consistently "terminating".
Nothing is - by design - consistently terminating vs. nonterminating in PowerShell:
The point of the $ErrorActionPreference preference variable / -ErrorAction parameter is to control the "terminatingness" of the behavior as needed (albeit in an inconsistent manner, and inconsistent with the documentation, as discussed in the linked docs issue).
Does PowerShell search the whole call stack for a try{}catch{}?
From what I can tell, yes.
Try this simple example (all scripts are assumed to reside in the current location):
Script t2.ps1:
Get-Item-NoSuchParam # provoke statement-terminating error"With the defaults, I'm pretty sure I won't execute, due to the try / catch on the call stack"
Script t1.ps1:
try {
./t2.ps1
"What are you doing here?"
} catch {
'ouch'
}
Running ./t1.ps yields:
ouch
That is, the statement-terminating error inside ./t2.ps1 aborted both the t2.ps - due to the try / catch on the call stack - and, bubbling up, the the try block, ultimately executing only the catch block in t1.ps1.
Are there circumstances aside from a wrapping try{} that results in .ThrowTerminatingError terminating more than just the statement?
Not that I'm aware of, but do let us know if you find such cases.
Nothing is - by design - consistently terminating vs. nonterminating in PowerShell:
This might be down to semantics, but throw consistently terminates in my experience.
Try this simple example...
The trouble with that approach to answering this question is that it proves what happens in that particular set of circumstances and not much else. I can get a more applicable version of that kind of information from unit testing commands that use $PSCmdlet.ThrowTerminatingError(). But that information doesn't tell me where to expect surprises the way an analytical answer often does. It seems like an analytical answer to this question is possible.
This might be down to semantics, but throw consistently terminates in my experience.
Except if you use try / catch (so, yes, by defaultthrow is runspace-terminating).
Yes, throw is the "apex error" (what I call a script-terminating error / runspace-terminating error) - it directly throws a by default uncaught exception that terminates the runspace.
You can promote other types of errors (nonterminating / statement-terminating) to "apex errors" via $ErrorActionPreference = 'Stop' / -ErrorAction Stop.
In the end, you can catch them with try / catch , anywhere up the call stack.
If they're already statement-terminating errors, however, you needn't promote them - try / catch will catch them as-is (just as it catches script-terminating errors, but unlikenonterminating errors).
If you don't, they terminate the runspace.
If you find a scenario that doesn't fit this model (which is based on my experiments), let us know.
The trouble with that approach to answering this question is that it proves what happens in that particular set of circumstances and not much else. I can get a more applicable version of that kind of information from unit testing commands that use $PSCmdlet.ThrowTerminatingError(). But that information doesn't tell me where to expect surprises the way an analytical answer often does. It seems like an analytical answer to this question is possible.
Note that the automatic change from statement-terminating error to exception in the presence of trap or try/catch is a fundamental semantic for PowerShell.
To think of this in terms of exceptions is a bit confusing, because the use of exceptions is an implementation detail, given that PowerShell's native "error currency" is error records ([System.Management.Automation.ErrorRecord] instances).
Both trap and try / catch allow you to intercept statement-terminating errors and script-terminating errors - which in the presence of these constructs are ignored by default, unless you take explicit action : to truly generate a script-terminating error (unhandled exception), you must, in a trap block, explicitly execute break, and, in a catch block, explicitly execute Throw.
By contrast, using $ErrorActionPreference = 'Stop' is the only way to directly promote both nonterminating and statement-terminating errors directly to script-terminating errors (unhandled exceptions) (the equivalent of using Throw).
While you can also catch those with trap and try / catch, in their absence such promoted errors will terminate the script.
In other words: $ErrorActionPreference = 'Stop' doesn't just promote nonterminating errors to statement-terminating errors, it promotes both types to script-terminating errors.
Activity
iSazonov commentedon Feb 3, 2018
I believe we terminating pipeline #2860 (comment)
alx9r commentedon Feb 3, 2018
If it were that simple, then the output of
would be
statement afternotcatch. The output iscatchindicating that more happened with flow of control than merely terminating the pipeline established by1 | a.mklement0 commentedon Feb 25, 2018
You should view this from the perspective of the
try/catchstatement:If a terminating error occurs - whether script- or statement-terminating - the entire
tryblock is exited at the point the error occurs and execution resumes in thecatchblock.(A nonterminating error, by contrast, is not caught.)
Except for the terminating-vs.-nonterminating error distinction, this is consistent with the usual
try/catchsemantics in, say, C#.alx9r commentedon Feb 25, 2018
Michael Klement (@mklement0)
Just to make sure we're on the same page, the situation is at least this complicated:
Write-Errorthrowcatch{}$PSCmdlet.ThrowTerminatingErrorcatch{}It seems to me that
$PSCmdlet.ThrowTerminating()is neither consistently "non-terminating" nor consistently "terminating".I'm not proficient at C#. Are there C# statements that continue to the next statement if there is no surrounding
try{}block but jump tocatch{}otherwise? That's what$PSCmdlet.ThrowTerminating()seems to do. I would consider PowerShellthrow's behavior consistent with usual try/catch semantics, but not$PSCmdlet.ThrowTerminating().This much is evident from the repro in my OP. But it doesn't really answer these questions from my OP:
Does PowerShell search the whole call stack for a try{}catch{}? Are there circumstances aside from a wrapping try{} that results in .ThrowTerminatingError terminating more than just the statement?
mklement0 commentedon Feb 26, 2018
So far, I don't see anything that contradicts my explanation.
Nothing is - by design - consistently terminating vs. nonterminating in PowerShell:
The point of the
$ErrorActionPreferencepreference variable /-ErrorActionparameter is to control the "terminatingness" of the behavior as needed (albeit in an inconsistent manner, and inconsistent with the documentation, as discussed in the linked docs issue).From what I can tell, yes.
Try this simple example (all scripts are assumed to reside in the current location):
Script
t2.ps1:Script
t1.ps1:Running
./t1.psyields:That is, the statement-terminating error inside
./t2.ps1aborted both thet2.ps- due to thetry/catchon the call stack - and, bubbling up, the thetryblock, ultimately executing only thecatchblock int1.ps1.Not that I'm aware of, but do let us know if you find such cases.
alx9r commentedon Feb 26, 2018
This might be down to semantics, but
throwconsistently terminates in my experience.The trouble with that approach to answering this question is that it proves what happens in that particular set of circumstances and not much else. I can get a more applicable version of that kind of information from unit testing commands that use
$PSCmdlet.ThrowTerminatingError(). But that information doesn't tell me where to expect surprises the way an analytical answer often does. It seems like an analytical answer to this question is possible.mklement0 commentedon Feb 26, 2018
Except if you use
try/catch(so, yes, by defaultthrowis runspace-terminating).Yes,
throwis the "apex error" (what I call a script-terminating error / runspace-terminating error) - it directly throws a by default uncaught exception that terminates the runspace.You can promote other types of errors (nonterminating / statement-terminating) to "apex errors" via
$ErrorActionPreference = 'Stop'/-ErrorAction Stop.In the end, you can catch them with
try/catch, anywhere up the call stack.try/catchwill catch them as-is (just as it catches script-terminating errors, but unlike nonterminating errors).If you don't, they terminate the runspace.
If you find a scenario that doesn't fit this model (which is based on my experiments), let us know.
I don't know what you mean.
alx9r commentedon Feb 26, 2018
You model seems to fit. I think you almost have me convinced. :)
Thanks for your help Michael Klement (@mklement0)!
alx9r commentedon Mar 14, 2018
BrucePay wrote in PowerShell/PowerShell#6286(comment):
mklement0 commentedon Mar 15, 2018
(Replacing my earlier comment; got confused)
To think of this in terms of exceptions is a bit confusing, because the use of exceptions is an implementation detail, given that PowerShell's native "error currency" is error records (
[System.Management.Automation.ErrorRecord]instances).Both
trapandtry/catchallow you to intercept statement-terminating errors and script-terminating errors - which in the presence of these constructs are ignored by default, unless you take explicit action : to truly generate a script-terminating error (unhandled exception), you must, in atrapblock, explicitly executebreak, and, in acatchblock, explicitly executeThrow.By contrast, using
$ErrorActionPreference = 'Stop'is the only way to directly promote both nonterminating and statement-terminating errors directly to script-terminating errors (unhandled exceptions) (the equivalent of usingThrow).While you can also catch those with
trapandtry/catch, in their absence such promoted errors will terminate the script.$ErrorActionPreference = 'Stop'doesn't just promote nonterminating errors to statement-terminating errors, it promotes both types to script-terminating errors.