Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Short description
Describes the types of errors in PowerShell and the mechanisms for handling them.
Long description
PowerShell distinguishes three categories of errors:
- Non-terminating errors
- Statement-terminating errors
- Script-terminating errors
Understanding the distinction is essential for writing reliable scripts and modules, because each category has different default behavior and requires different handling techniques.
Additionally, external (native) programs report failure through exit codes, which PowerShell tracks separately from its own error system.
Types of errors
Non-terminating errors
A non-terminating error reports a problem but doesn't stop the pipeline. The command continues processing subsequent input objects. Non-terminating errors are generated by:
- The
Write-Errorcmdlet - The
$PSCmdlet.WriteError()method in advanced functions - Cmdlets that encounter recoverable failures on individual input objects
By default, PowerShell displays the error message and continues execution.
# Non-terminating error: the pipeline continues after the failure
'file1.txt', 'noSuchFile.txt', 'file3.txt' | ForEach-Object {
Get-Content $_ -ErrorAction Continue
}
In this example, Get-Content reports a non-terminating error for
noSuchFile.txt and then continues processing file3.txt.
Non-terminating errors do not trigger catch or trap by default.
Statement-terminating errors
A statement-terminating error stops the current statement (pipeline) from running, but execution continues at the next statement in the script. Statement-terminating errors are generated by:
- The
$PSCmdlet.ThrowTerminatingError()method in advanced functions and compiled cmdlets - Engine errors such as
CommandNotFoundException(calling a command that doesn't exist) andParameterBindingException(invalid parameter arguments) - .NET method calls that throw exceptions, such as
[int]::Parse('abc')
# Statement-terminating error: Get-Item fails, but the next statement runs
Get-Item -Path 'C:\NoSuchFile.txt'
Write-Output 'This still runs'
Statement-terminating errors can be caught by try/catch and trap.
Note
.ThrowTerminatingError() doesn't consult the -ErrorAction parameter
(except for the Break value, which enters the debugger). However,
$ErrorActionPreference does apply to statement-terminating errors
through the engine's statement-level handler. For example,
$ErrorActionPreference = 'SilentlyContinue' can suppress a
statement-terminating error so that the script continues at the next
statement. The -ErrorAction parameter can't do this. For details, see
The $ErrorActionPreference asymmetry.
Script-terminating errors
A script-terminating error unwinds the entire call stack. Execution stops
completely unless the error is caught by a try/catch block or trap
statement. Script-terminating errors are generated by:
- The
throwkeyword - Parse errors (syntax errors that prevent the script from being compiled)
- Non-terminating errors escalated by
-ErrorAction Stopor$ErrorActionPreference = 'Stop'in non-advanced contexts. For more information, see How escalation works. - Certain critical engine failures
# Script-terminating error: throw unwinds the call stack
function Test-Throw {
throw 'Critical failure'
Write-Output 'This never runs'
}
Test-Throw
Write-Output 'This never runs either (unless caught)'
The throw keyword generates a script-terminating error by default. However,
$ErrorActionPreference can suppress throw when set to SilentlyContinue
or Ignore. When calling an advanced function with
-ErrorAction SilentlyContinue, the parameter translates to a scope-local
$ErrorActionPreference value, so it also suppresses throw inside that
function.
Note
Even with $ErrorActionPreference = 'Ignore', a throw that's suppressed
still records an entry in $Error. The Ignore value only prevents $Error
recording for non-terminating errors.
Important
The terms statement-terminating and script-terminating describe the scope
of impact, not the severity of the error. A statement-terminating error stops
one statement. A script-terminating error stops the entire script and its
callers. Both can be caught by try/catch.
External program errors
External (native) programs don't participate in PowerShell's error system
directly. They report failure through a non-zero exit code, which PowerShell
stores in the $LASTEXITCODE automatic variable.
git clone https://example.com/nonexistent.git 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Error "git failed with exit code $LASTEXITCODE"
}
By default, a non-zero exit code from a native program:
- Sets
$?to$false - Does not generate an
ErrorRecordin$Error - Does not trigger
catchortrap
PowerShell 7.3 added the experimental preference variable
$PSNativeCommandUseErrorActionPreference, which became a stable feature in
7.4. When you set this variable to $true, it causes a non-zero exit code to
emit a non-terminating error whose message states the specific exit code (a
NativeCommandExitException). This error respects $ErrorActionPreference, so
setting it to Stop promotes the error to a script-terminating error that can
be caught with try/catch.
Error state variables
PowerShell maintains several automatic variables that reflect the current error state.
$?
Contains $true if the last operation succeeded and $false if it produced
any error (non-terminating or terminating). For native commands, $? is set
based on the exit code: $true for exit code 0, $false otherwise.
Get-Item -Path 'C:\NoSuchFile.txt' 2>$null
$? # False
$Error
An ArrayList that stores the most recent error records, with the most recent
error at index 0. The list holds up to $MaximumErrorCount entries (default
256).
All terminating errors are added to $Error. For terminating errors, Ignore
suppresses display but still records the error in $Error. All non-terminating
are added to $Error unless -ErrorAction Ignore is used on non-terminating
errors, which prevents both display and recording.
$LASTEXITCODE
Contains the exit code of the last native program that ran. A value of 0
conventionally indicates success. Any non-zero value indicates failure. This
variable isn't affected by PowerShell cmdlet errors.
Control error behavior
The -ErrorAction common parameter
The -ErrorAction common parameter overrides $ErrorActionPreference for a
single command. It controls how PowerShell responds to non-terminating
errors from that command.
| Value | Behavior |
|---|---|
Continue |
Display the error and continue (default) |
SilentlyContinue |
Suppress display, add to $Error, continue |
Ignore |
Suppress display and don't add to $Error |
Stop |
Escalate to a terminating error (see How escalation works) |
Inquire |
Prompt the user for a decision |
Break |
Enter the debugger |
-ErrorAction doesn't change the behavior of errors generated by
$PSCmdlet.ThrowTerminatingError(). Those errors are always
statement-terminating regardless of the caller's preference.
The $ErrorActionPreference variable
The $ErrorActionPreference preference variable applies to all commands in the
current scope and child scopes. It accepts the same values as -ErrorAction.
$ErrorActionPreference = 'Stop'
# All non-terminating errors in this scope now become terminating
Write-Error 'This now throws' # Generates ActionPreferenceStopException
When -ErrorAction is specified on a command, it takes precedence over
$ErrorActionPreference for that command.
How escalation works
When -ErrorAction Stop or $ErrorActionPreference = 'Stop' is in effect,
PowerShell converts non-terminating errors into terminating errors using the
following mechanism:
- A cmdlet calls
WriteError()internally to emit a non-terminating error. - The engine checks the effective
ErrorActionpreference for the command. - Because the preference is
Stop, the engine creates anActionPreferenceStopExceptionthat wraps the original error record. - If caught by
catch, the original error information is accessible through$_.Exception.ErrorRecord.
The scope of the escalated error depends on context:
- In non-advanced scripts, functions, or script blocks, setting
$ErrorActionPreference = 'Stop'escalates to a script-terminating error. The error propagates up the call stack. - In advanced functions and script blocks (those with
[CmdletBinding()]), the error remains statement-terminating. Execution continues at the next statement after the call. - Passing
-ErrorAction Stopto an advanced function has the same effect as setting$ErrorActionPreference = 'Stop'inside it, because-ErrorActiontranslates to a scope-local$ErrorActionPreferencevalue.
Escalation examples
NON-advanced: script-terminating ('after' does NOT print)
& { param() $ErrorActionPreference = 'Stop' Get-Item 'NoSuchPath' } 2>$null 'after'ADVANCED: statement-terminating ('after' DOES print)
& { [CmdletBinding()] param() $ErrorActionPreference = 'Stop'; Get-Item 'NoSuchPath' } 2>$null 'after'Without
-ErrorAction Stop: non-terminating, catch doesn't runtry { Write-Error 'This is non-terminating' Write-Output 'Execution continues' } catch { Write-Output "Caught: $_" # Not reached }With
-ErrorAction Stop: escalated to terminatingtry { Write-Error 'This becomes terminating' -ErrorAction Stop } catch { Write-Output "Caught: $_" # Reached }
Escalated errors can be caught by their original exception type. The engine
unwraps the ActionPreferenceStopException to find the underlying exception:
try {
Get-Item -Path 'C:\NoSuchFile.txt' -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] {
Write-Output "File not found: $($_.Exception.Message)"
}
The $ErrorActionPreference asymmetry
The -ErrorAction parameter and the $ErrorActionPreference variable behave
differently with terminating errors. It's important to understand this
asymmetry:
-ErrorActiononly affects non-terminating errors. When a cmdlet calls$PSCmdlet.ThrowTerminatingError(), the-ErrorActionparameter is ignored (except forBreak, which enters the debugger). The error is always thrown.$ErrorActionPreferenceaffects both non-terminating and statement-terminating errors. The engine's statement-level error handler reads$ErrorActionPreference(not the-ErrorActionparameter) and can suppress a statement-terminating error when the value isSilentlyContinueorIgnore.
function Test-Asymmetry {
[CmdletBinding()]
param()
$er = [System.Management.Automation.ErrorRecord]::new(
[System.InvalidOperationException]::new('test error'),
'TestError',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$null
)
$PSCmdlet.ThrowTerminatingError($er)
}
# -ErrorAction SilentlyContinue does NOT suppress the error:
Test-Asymmetry -ErrorAction SilentlyContinue # Error is still thrown
# $ErrorActionPreference DOES suppress the error:
$ErrorActionPreference = 'SilentlyContinue'
Test-Asymmetry # Error is silently suppressed, script continues
$ErrorActionPreference = 'Continue'
Important
$ErrorActionPreference can't suppress errors that have
SuppressPromptInInterpreter set to true. These always propagate
regardless of the preference variable. Examples of this type of error
include:
ActionPreferenceStopExceptionfrom-ErrorAction Stopescalation- Errors inside PowerShell class methods
PipelineStoppedException
Handle errors
try/catch/finally
Use try/catch/finally to handle statement-terminating and script-terminating
errors. When an error occurs inside a try block, PowerShell searches for a
matching catch block. The finally block always runs, whether or not an
error occurred.
try {
$result = Get-Content -Path 'data.txt' -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning 'Data file not found, using defaults.'
$result = 'default'
}
catch {
Write-Warning "Unexpected error: $_"
}
finally {
Write-Verbose 'Cleanup complete.' -Verbose
}
Inside a try block, the engine sets an internal flag that causes non-
terminating errors escalated by -ErrorAction Stop or
$ErrorActionPreference = 'Stop' to propagate to the catch block. This
is designed behavior, not a special case.
For full syntax details, see about_Try_Catch_Finally.
trap
The trap statement handles terminating errors at the scope level. When an
error occurs anywhere in the enclosing scope, the trap block runs.
- Default (no
breakorcontinue): The error is displayed and execution continues at the next statement after the one that caused the error. continuein the trap: Suppresses the error message and resumes at the next statement.breakin the trap: The error propagates to the parent scope.
trap [System.Management.Automation.CommandNotFoundException] {
Write-Warning "Command not found: $($_.TargetObject)"
continue
}
NonsenseCommand # Trap fires, execution continues
Write-Output 'This runs because the trap used continue'
For full syntax details, see about_Trap.
Reporting errors in functions and scripts
When writing functions and scripts, choose the error-reporting mechanism that matches the severity of the failure.
Non-terminating - use Write-Error
Use Write-Error when the function can continue processing other input. This
is appropriate for pipeline functions that process multiple objects and
encounter failures on individual items.
function Test-Path-Safe {
[CmdletBinding()]
param([Parameter(ValueFromPipeline)][string]$Path)
process {
if (-not (Test-Path $Path)) {
Write-Error "Path not found: $Path"
return
}
$Path
}
}
Note
In advanced functions (those with [CmdletBinding()]), use
$PSCmdlet.WriteError() instead of Write-Error to ensure that $? is
properly set to $false in the caller's scope. The Write-Error cmdlet
doesn't always set $? correctly.
Statement-terminating - use $PSCmdlet.ThrowTerminatingError()
Use $PSCmdlet.ThrowTerminatingError() when the function can't continue at all
but the caller should decide how to handle the failure. This is the recommended
approach in advanced functions.
function Get-Config {
[CmdletBinding()]
param([string]$Path)
if (-not (Test-Path $Path)) {
$er = [System.Management.Automation.ErrorRecord]::new(
[System.IO.FileNotFoundException]::new("Config file not found: $Path"),
'ConfigNotFound',
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
$Path
)
$PSCmdlet.ThrowTerminatingError($er)
}
Get-Content $Path | ConvertFrom-Json
}
After the error leaves the function, the caller treats it as a non-terminating
error by default. The caller can escalate it with -ErrorAction Stop.
Script-terminating - use throw
Use throw when recovery isn't possible and the entire script should stop.
$config = Get-Content 'config.json' -ErrorAction SilentlyContinue |
ConvertFrom-Json
if (-not $config) {
throw 'Cannot proceed without a valid configuration file.'
}
Which mechanism to use
- When processing multiple inputs where some may fail, use
Write-Erroror$PSCmdlet.WriteError(). - If the function can't continue, use
$PSCmdlet.ThrowTerminatingError()and let the caller decide how to handle it. - If the entire script must stop immediately, use
throw.
Summary of error types
The following tables summarize the properties and behaviors of the different error types in PowerShell.
Non-terminating error
Non-terminating errors can be generated by Write-Error or
$PSCmdlet.WriteError().
| Attribute | Description |
|---|---|
| Scope of impact | Pipeline continues |
Caught by catch |
No (unless escalated) |
Caught by trap |
No (unless escalated) |
Added to $Error |
Yes (unless Ignore) |
Sets $? to $false |
Yes |
Affected by -ErrorAction |
Yes |
Affected by $ErrorActionPreference |
Yes |
Statement-terminating error
Statement-terminating errors can be generated by
ThrowTerminatingError(), engine errors, .NET method exceptions, or
-ErrorAction Stop in advanced contexts.
| Attribute | Description |
|---|---|
| Scope of impact | Current statement stops; script continues |
Caught by catch |
Yes |
Caught by trap |
Yes |
Added to $Error |
Yes |
Sets $? to $false |
Yes |
Affected by -ErrorAction |
No (Break only) |
Affected by $ErrorActionPreference |
Yes (can suppress) |
Script-terminating error
Script-terminating errors can be generated by throw, parse errors, or
-ErrorAction Stop in non-advanced contexts.
| Attribute | Description |
|---|---|
| Scope of impact | Call stack unwinds |
Caught by catch |
Yes |
Caught by trap |
Yes |
Added to $Error |
Yes |
Sets $? to $false |
Yes |
Affected by -ErrorAction |
No |
Affected by $ErrorActionPreference |
throw: Yes (can suppress) |
Affected by $ErrorActionPreference |
Escalated: depends on context |