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
Is there a way to distinguish between caught script- and statement-terminating errors? #6286
I've been experimenting with an improved test fixture for PowerShell commands. I am hoping to find a way to distinguish between script- and statement-terminating errors (using the terminology from MicrosoftDocs/PowerShell-Docs#1583). A (very) simplified version of the core of the test fixture looks something like this:
The obvious way to distinguish between a script- and statement-terminating error is to test control flow behavior when the error occurs (see #6098 for an example). The try {} around $CommandUnderTest in TestFixture is necessary to capture and record exceptions as they are thrown, and keep flow of control within the test fixture. Unfortunately, the presence of that try{} block precludes the using control flow behavior to distinguish script- from statement-terminating errors because both kinds of errors result in the same control flow (ie. both jump to the catch block).
I was hoping that .WasThrownByThrowStatement would provide enough information to distinguish between the two. Unfortunately per #6288 and #3647 (comment) that is not currently sufficient to distinguish.
Is there a way to distinguish between script- and statement-terminating errors once they are caught in the scriptblock?
Is there a way to preserve the distinction in control flow behavior between a statement- and script-terminating errors and retain control of flow in the fixture?
Is there some other way to distinguish between script- and statement-terminating errors in this scenario?
Hi alx9r, There aren't really three kinds of errors as such, just three dispositions of one kind of error. For example, if a command writes an error to error output, it's non-terminating. If -ErrorAction Stop is specified, that non-terminating write becomes a statement-terminating error. If there is a trap or try/catch in scope, the statement-terminating error is thrown to the catch block. In this scenario, the same error object eventually becomes all three "types" as it flows through the system - it began as non-terminating, became terminating and then became an exception. (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.)
If -ErrorAction Stop is specified, that non-terminating write becomes a statement-terminating error.
In my experimentation Write-Error -ea Stop, for example, does not cause a statement-terminating error.
Rather, Write-Error -ea Stop seems to always throw an exception. In my experimentation the effect of -ErrorAction has been consistent with being an implementation detail of the command to which it is passed. Do you mean $ErrorActionPreference = 'Stop' here?
In this scenario, the same error object eventually becomes all three "types" as it flows through the system - it began as non-terminating, became terminating and then became an exception.
I think I understand the escalation concept you are describing. I have not, however, come across any one error source that observably escalates from non-terminating to statement-terminating to exception in the idealized way you describe. Do you have an example of a command that observably behaves the way you describe? Or are you describing an unobservable internal escalation process here?
I arrived at the following table empirically (test code here).
ref.
Statement
In try{}?
ErrorActionPreference
Exception Thrown by statement?
1
throw
x
not SilentlyContinue
yes
2
throw
yes
SilentlyContinue
yes
3
throw
no
SilentlyContinue
no
4
ThrowTerminatingError()
yes
x
yes
5
ThrowTerminatingError()
no
Stop
yes
6
ThrowTerminatingError()
no
Continue
no
7
Write-Error -ea Stop
x
x
yes
8
Write-Error
x
Stop
yes
9
Write-Error
x
Continue
no
x = does not matter
Another way to ask my original question is as follows:
Is there a way to distinguish between a caught exception thrown by throw (ie. ref. 1 and 2) and an exception thrown by ThrowTerminatingError() (ie. ref. 4)?
In yet other words, is there a way to infer from a caught exception whether that exception would have been thrown in the absence of the try{}catch{} that caught it?
Preference variable$ErrorActionPreference = 'Stop' makes both nonterminating and statement-terminating errors throw an unhandled-by-default exception (for which I coined the term script-terminating error, which I'll use in the remainder of this post).
Common parameter-ErrorAction Stop, by contrast, only affects nonterminating errors and promotes them directly to script-terminating errors.
This directly contradicts the docs, which do not distinguish between preference-variable and common-parameter use and categorically claim that Stop only affects nonterminating errors (which is only true with the common parameter).
You can't help but wonder if the original intent was:
to never affect statement-terminating errors with preference variable$ErrorActionPreference = 'Stop', in line with the common parameter's behavior and the documentation.
to have both $ErrorActionPreference = 'Stop' and -ErrorAction Stop promote nonterminating errors to statement-terminating ones rather than to script-terminating ones.
This would explain why -ErrorAction Stop has no effect on statement-terminating errors: they already are statement-terminating errors; in other words: they already are in the desired target state.
In other words: the original intent may have been to never terminate the script by default , except with the use of Throw, and to instead require use of try/catch or trap in order to effect termination.
The Throw documentation confusingly talks about generating a "terminating error" too, without pointing out the fundamental distinction from a statement-terminating error.
Additionally, the documentation's example use case shows use of Throw to enforce a mandatory parameter without prompting; of course, using this technique results in a script-terminating error, whereas cmdlets normally only generate statement-terminating errors, even in the face of incorrect syntax and, if the PowerShell instance was invoked with -NonInteractive, in the absence of mandatory parameters (declared without Throw).
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.
microsoft-github-policy-service commented on Nov 16, 2023
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.
microsoft-github-policy-service commented on Nov 23, 2023
Activity
iSazonov commentedon Mar 2, 2018
Related #4837, #4781
Dup #3158alx9r commentedon Mar 2, 2018
Ilya (@iSazonov) This issue is not a dup of #3158:
$Erroralx9r commentedon Mar 13, 2018
Michael Klement (@mklement0) If you have the time, could you take a look at this and see if you have any ideas for how to achieve this?
BrucePay commentedon Mar 14, 2018
Hi alx9r, There aren't really three kinds of errors as such, just three dispositions of one kind of error. For example, if a command writes an error to error output, it's non-terminating. If
-ErrorAction Stopis specified, that non-terminating write becomes a statement-terminating error. If there is a trap or try/catch in scope, the statement-terminating error is thrown to the catch block. In this scenario, the same error object eventually becomes all three "types" as it flows through the system - it began as non-terminating, became terminating and then became an exception. (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.)alx9r commentedon Mar 15, 2018
Bruce Payette (@BrucePay)
In my experimentation
Write-Error -ea Stop, for example, does not cause a statement-terminating error.Rather,
Write-Error -ea Stopseems to always throw an exception. In my experimentation the effect of-ErrorActionhas been consistent with being an implementation detail of the command to which it is passed. Do you mean$ErrorActionPreference = 'Stop'here?I think I understand the escalation concept you are describing. I have not, however, come across any one error source that observably escalates from non-terminating to statement-terminating to exception in the idealized way you describe. Do you have an example of a command that observably behaves the way you describe? Or are you describing an unobservable internal escalation process here?
I arrived at the following table empirically (test code here).
try{}?ErrorActionPreferencethrowSilentlyContinuethrowSilentlyContinuethrowSilentlyContinueThrowTerminatingError()ThrowTerminatingError()StopThrowTerminatingError()ContinueWrite-Error -ea StopWrite-ErrorStopWrite-ErrorContinuex = does not matter
Another way to ask my original question is as follows:
Is there a way to distinguish between a caught exception thrown by
throw(ie. ref. 1 and 2) and an exception thrown byThrowTerminatingError()(ie. ref. 4)?In yet other words, is there a way to infer from a caught exception whether that exception would have been thrown in the absence of the
try{}catch{}that caught it?mklement0 commentedon Mar 15, 2018
alx9r: Nice writeup; to add to the general part:
Indeed, to recap from the error-handling saga:
Preference variable
$ErrorActionPreference = 'Stop'makes both nonterminating and statement-terminating errors throw an unhandled-by-default exception (for which I coined the term script-terminating error, which I'll use in the remainder of this post).Common parameter
-ErrorAction Stop, by contrast, only affects nonterminating errors and promotes them directly to script-terminating errors.This directly contradicts the docs, which do not distinguish between preference-variable and common-parameter use and categorically claim that
Stoponly affects nonterminating errors (which is only true with the common parameter).You can't help but wonder if the original intent was:
to never affect statement-terminating errors with preference variable
$ErrorActionPreference = 'Stop', in line with the common parameter's behavior and the documentation.to have both
$ErrorActionPreference = 'Stop'and-ErrorAction Stoppromote nonterminating errors to statement-terminating ones rather than to script-terminating ones.-ErrorAction Stophas no effect on statement-terminating errors: they already are statement-terminating errors; in other words: they already are in the desired target state.In other words: the original intent may have been to never terminate the script by default , except with the use of
Throw, and to instead require use oftry/catchortrapin order to effect termination.Throwdocumentation confusingly talks about generating a "terminating error" too, without pointing out the fundamental distinction from a statement-terminating error.Throwto enforce a mandatory parameter without prompting; of course, using this technique results in a script-terminating error, whereas cmdlets normally only generate statement-terminating errors, even in the face of incorrect syntax and, if the PowerShell instance was invoked with-NonInteractive, in the absence of mandatory parameters (declared withoutThrow).Bruce Payette (@BrucePay), can you shed light on this?
Move powershell to .NET Core 2.0 (PowerShell#3556)
microsoft-github-policy-service commentedon Nov 16, 2023
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.
microsoft-github-policy-service commentedon Nov 16, 2023
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.
microsoft-github-policy-service commentedon Nov 16, 2023
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.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.
Move powershell to .NET Core 2.0 (PowerShell#3556)