Skip to content

Commit 4f51cce

Browse files
SufficientDaikonclaude
authored andcommitted
Comprehensive accuracy overhaul: escalation semantics, asymmetry, parse errors
Major corrections verified against PowerShell engine source code: - Fix: -ErrorAction Stop produces script-terminating errors, not statement-terminating. ActionPreferenceStopException sets SuppressPromptInInterpreter=true, causing call-stack unwinding. Fixed across all 4 affected files. - Add: New "$ErrorActionPreference asymmetry" section in about_Error_Handling documenting that $ErrorActionPreference applies to BOTH non-terminating and statement-terminating errors, while -ErrorAction only affects non-terminating errors. Includes runnable demo and IMPORTANT callout for exceptions (SuppressPromptInInterpreter, PS classes, PipelineStoppedException). - Add: Parse errors to script-terminating generators list (per mklement0 review comment). - Fix: about_Preference_Variables Ignore bullet incorrectly stated "Ignore isn't a valid value for $ErrorActionPreference" — it is accepted (only Suspend is rejected per ExecutionContext.cs). - Fix: NOTE block on ThrowTerminatingError now correctly explains that $ErrorActionPreference can suppress it via the engine's statement-level handler in MiscOps.CheckActionPreference. - Fix: Summary table updated — -ErrorAction Stop moved from statement-terminating to script-terminating column, new row added for $ErrorActionPreference behavior per error type. - Fix: everything-about-exceptions.md — three remaining instances of vague "terminating error" updated to "script-terminating error" for throw and -ErrorAction Stop contexts. All claims verified with live PowerShell 7.x tests: 1. -ErrorAction Stop: next statement in scriptblock does NOT run (script-terminating) 2. Parse errors: caught as MethodInvocationException 3. $ErrorActionPreference='Ignore': accepted without error 4. $ErrorActionPreference='SilentlyContinue': suppresses ThrowTerminatingError 5. -ErrorAction SilentlyContinue: does NOT suppress ThrowTerminatingError Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c662119 commit 4f51cce

File tree

4 files changed

+77
-22
lines changed

4 files changed

+77
-22
lines changed

reference/7.6/Microsoft.PowerShell.Core/About/about_CommonParameters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ catchable by `try/catch`. For more information about error categories, see
144144
- `SilentlyContinue` suppresses the error message and continues executing the
145145
command.
146146
- `Stop` displays the error message and stops executing the command. The
147-
`Stop` value escalates the non-terminating error to a statement-terminating
147+
`Stop` value escalates the non-terminating error to a script-terminating
148148
error by generating an `ActionPreferenceStopException`. The error can then
149149
be caught by a `try/catch` block or `trap` statement.
150150
- `Suspend` is only available for workflows which aren't supported in

reference/7.6/Microsoft.PowerShell.Core/About/about_Error_Handling.md

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ Statement-terminating errors are generated by:
6060
compiled cmdlets
6161
- Engine errors such as `CommandNotFoundException` (calling a command that does
6262
not exist) and `ParameterBindingException` (invalid parameter arguments)
63-
- Non-terminating errors **escalated** by `-ErrorAction Stop` or
64-
`$ErrorActionPreference = 'Stop'` (see [How escalation works][05])
6563
- .NET method calls that throw exceptions, such as `[int]::Parse('abc')`
6664

6765
```powershell
@@ -73,10 +71,14 @@ Write-Output 'This still runs'
7371
Statement-terminating errors can be caught by `try/catch` and `trap`.
7472

7573
> [!NOTE]
76-
> `.ThrowTerminatingError()` does not consult `-ErrorAction` or
77-
> `$ErrorActionPreference` (except for the `Break` value, which enters the
78-
> debugger). Once a cmdlet calls `.ThrowTerminatingError()`, the error is
79-
> always thrown regardless of the caller's preferences.
74+
> `.ThrowTerminatingError()` does not consult the `-ErrorAction` parameter
75+
> (except for the `Break` value, which enters the debugger). However,
76+
> `$ErrorActionPreference` _does_ apply to statement-terminating errors
77+
> through the engine's statement-level handler. For example,
78+
> `$ErrorActionPreference = 'SilentlyContinue'` can suppress a
79+
> statement-terminating error so that the script continues at the next
80+
> statement. The `-ErrorAction` parameter cannot do this. For details, see
81+
> [The $ErrorActionPreference asymmetry][09].
8082
8183
### Script-terminating errors
8284

@@ -85,6 +87,9 @@ completely unless the error is caught by a `try/catch` block or `trap`
8587
statement. Script-terminating errors are generated by:
8688

8789
- The `throw` keyword
90+
- Parse errors (syntax errors that prevent the script from being compiled)
91+
- Non-terminating errors **escalated** by `-ErrorAction Stop` or
92+
`$ErrorActionPreference = 'Stop'` (see [How escalation works][05])
8893
- Certain critical engine failures
8994

9095
```powershell
@@ -204,14 +209,15 @@ When `-ErrorAction` is specified on a command, it takes precedence over
204209
### How escalation works
205210

206211
When `-ErrorAction Stop` or `$ErrorActionPreference = 'Stop'` is in effect,
207-
PowerShell converts non-terminating errors into statement-terminating errors
212+
PowerShell converts non-terminating errors into script-terminating errors
208213
using the following mechanism:
209214

210215
1. A cmdlet calls `WriteError()` internally to emit a non-terminating error.
211216
1. The engine checks the effective `ErrorAction` preference for the command.
212217
1. Because the preference is `Stop`, the engine creates an
213218
`ActionPreferenceStopException` that wraps the original error record.
214-
1. This exception propagates as a statement-terminating error.
219+
1. This exception has `SuppressPromptInInterpreter` set to `true`, which
220+
causes it to propagate up the call stack like a script-terminating error.
215221
1. If caught by `catch`, the original error information is accessible through
216222
`$_.Exception.ErrorRecord`.
217223

@@ -225,7 +231,7 @@ try {
225231
}
226232
227233
try {
228-
# With -ErrorAction Stop: escalated to statement-terminating
234+
# With -ErrorAction Stop: escalated to script-terminating
229235
Write-Error 'This becomes terminating' -ErrorAction Stop
230236
} catch {
231237
Write-Output "Caught: $_" # Reached
@@ -243,6 +249,51 @@ try {
243249
}
244250
```
245251

252+
### The $ErrorActionPreference asymmetry
253+
254+
The `-ErrorAction` parameter and the `$ErrorActionPreference` variable behave
255+
differently with terminating errors. This is an important asymmetry:
256+
257+
- **`-ErrorAction`** only affects **non-terminating** errors. When a cmdlet
258+
calls `$PSCmdlet.ThrowTerminatingError()`, the `-ErrorAction` parameter is
259+
ignored (except for `Break`, which enters the debugger). The error is always
260+
thrown.
261+
262+
- **`$ErrorActionPreference`** affects **both** non-terminating and
263+
statement-terminating errors. The engine's statement-level error handler
264+
reads `$ErrorActionPreference` (not the `-ErrorAction` parameter) and can
265+
suppress a statement-terminating error when the value is `SilentlyContinue`
266+
or `Ignore`.
267+
268+
```powershell
269+
function Test-Asymmetry {
270+
[CmdletBinding()]
271+
param()
272+
$er = [System.Management.Automation.ErrorRecord]::new(
273+
[System.InvalidOperationException]::new('test error'),
274+
'TestError',
275+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
276+
$null
277+
)
278+
$PSCmdlet.ThrowTerminatingError($er)
279+
}
280+
281+
# -ErrorAction SilentlyContinue does NOT suppress the error:
282+
Test-Asymmetry -ErrorAction SilentlyContinue # Error is still thrown
283+
284+
# $ErrorActionPreference DOES suppress the error:
285+
$ErrorActionPreference = 'SilentlyContinue'
286+
Test-Asymmetry # Error is silently suppressed, script continues
287+
$ErrorActionPreference = 'Continue'
288+
```
289+
290+
> [!IMPORTANT]
291+
> `$ErrorActionPreference` cannot suppress errors that have
292+
> `SuppressPromptInInterpreter` set to `true` (such as
293+
> `ActionPreferenceStopException` from `-ErrorAction Stop` escalation),
294+
> errors inside PowerShell class methods, or `PipelineStoppedException`.
295+
> These always propagate regardless of the preference variable.
296+
246297
## Handling errors
247298

248299
### try/catch/finally
@@ -382,13 +433,14 @@ if (-not $config) {
382433

383434
| Property | Non-terminating | Statement-terminating | Script-terminating |
384435
|----------|----------------|----------------------|-------------------|
385-
| Generated by | `Write-Error`, `WriteError()` | `ThrowTerminatingError()`, engine errors, `-ErrorAction Stop` | `throw` |
436+
| Generated by | `Write-Error`, `WriteError()` | `ThrowTerminatingError()`, engine errors, .NET method exceptions | `throw`, parse errors, `-ErrorAction Stop` escalation |
386437
| Scope of impact | Pipeline continues | Current statement stops; script continues | Call stack unwinds |
387438
| Caught by `catch` | No (unless escalated) | Yes | Yes |
388439
| Caught by `trap` | No (unless escalated) | Yes | Yes |
389440
| Added to `$Error` | Yes (unless `Ignore`) | Yes | Yes |
390441
| Sets `$?` to `$false` | Yes | Yes | Yes |
391-
| Affected by `-ErrorAction` | Yes | No (already terminating) | No |
442+
| Affected by `-ErrorAction` | Yes | No (`Break` only) | No |
443+
| Affected by `$ErrorActionPreference` | Yes | Yes (can suppress) | `throw`: No; escalated: No |
392444

393445
## See also
394446

@@ -409,3 +461,4 @@ if (-not $config) {
409461
[06]: about_Try_Catch_Finally.md
410462
[07]: about_Trap.md
411463
[08]: about_Throw.md
464+
[09]: #the-erroractionpreference-asymmetry

reference/7.6/Microsoft.PowerShell.Core/About/about_Preference_Variables.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,10 @@ The valid values are as follows:
388388
raised.
389389
- **Continue**: (Default) Displays the error message and continues executing.
390390
- **Ignore**: Suppresses the error message and continues to execute the
391-
command. The **Ignore** value is intended for per-command use, not for use as
392-
saved preference. **Ignore** isn't a valid value for the
393-
`$ErrorActionPreference` variable.
391+
command. Unlike **SilentlyContinue**, **Ignore** doesn't add the error
392+
message to the `$Error` automatic variable. The **Ignore** value is also
393+
valid for `$ErrorActionPreference`, where it suppresses both non-terminating
394+
and statement-terminating errors without recording them in `$Error`.
394395
- **Inquire**: Displays the error message and asks you whether you want to
395396
continue.
396397
- **SilentlyContinue**: No effect. The error message isn't displayed and

reference/docs-conceptual/learn/deep-dives/everything-about-exceptions.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ it.
4747
PowerShell has three categories of errors. A _non-terminating error_ (generated
4848
by `Write-Error`) adds an error to the error stream without stopping execution
4949
and does not trigger `catch`. A _statement-terminating error_ (generated by
50-
`$PSCmdlet.ThrowTerminatingError()` or by `-ErrorAction Stop` escalation) stops
51-
the current statement but allows the script to continue at the next statement.
52-
A _script-terminating error_ (generated by `throw`) unwinds the entire call
53-
stack. Both statement-terminating and script-terminating errors can be caught by
50+
`$PSCmdlet.ThrowTerminatingError()`, engine errors, or .NET method exceptions)
51+
stops the current statement but allows the script to continue at the next
52+
statement. A _script-terminating error_ (generated by `throw`, parse errors, or
53+
`-ErrorAction Stop` escalation) unwinds the entire call stack. Both
54+
statement-terminating and script-terminating errors can be caught by
5455
`try/catch`. For a comprehensive reference, see
5556
[about_Error_Handling](/powershell/module/microsoft.powershell.core/about/about_error_handling).
5657

@@ -74,7 +75,7 @@ function Start-Something
7475
}
7576
```
7677

77-
This creates a runtime exception that is a terminating error. It's handled by a `catch` in a
78+
This creates a runtime exception that is a script-terminating error. It's handled by a `catch` in a
7879
calling function or exits the script with a message like this.
7980

8081
```powershell
@@ -91,7 +92,7 @@ At line:1 char:1
9192
#### Write-Error -ErrorAction Stop
9293

9394
I mentioned that `Write-Error` doesn't throw a terminating error by default. If you specify
94-
`-ErrorAction Stop`, `Write-Error` generates a terminating error that can be handled with a
95+
`-ErrorAction Stop`, `Write-Error` generates a script-terminating error that can be handled with a
9596
`catch`.
9697

9798
```powershell
@@ -103,7 +104,7 @@ Thank you to Lee Dailey for reminding about using `-ErrorAction Stop` this way.
103104
#### Cmdlet -ErrorAction Stop
104105

105106
If you specify `-ErrorAction Stop` on any advanced function or cmdlet, it turns all `Write-Error`
106-
statements into terminating errors that stop execution or that can be handled by a `catch`.
107+
statements into script-terminating errors that stop execution or that can be handled by a `catch`.
107108

108109
```powershell
109110
Start-Something -ErrorAction Stop

0 commit comments

Comments
 (0)