Terminating and Non-Terminating Errors, ErrorAction, and Preferences
Build a precise PowerShell error model using ErrorAction, preference variables, $Error, ErrorRecord metadata, and narrow error-capture patterns.
Learning objectives
- Distinguish reporting an error from stopping execution.
- Use -ErrorAction values intentionally for one command.
- Explain the broader scope of $ErrorActionPreference.
- Inspect $Error and ErrorRecord properties as structured diagnostic evidence.
- Capture command-specific errors with -ErrorVariable.
- Avoid global suppression and design explicit optional-versus-required failure policy.
1. Not every error stops the operation that reported it
PowerShell distinguishes errors partly by how far they stop execution. A non-terminating error reports a problem but lets the command continue processing when possible. A terminating error stops the current statement or unwinds farther depending on the error category and context. This behavior matters more than the visual color of the message.
Write-Output 'before'
Get-Item -LiteralPath './definitely-missing-item' -ErrorAction Continue
Write-Output 'after'The missing item produces an error, but the next statement can still run. That is a useful default for commands that process many independent items, but it means “I saw an error” and “execution stopped” are not the same statement.
2. -ErrorAction changes non-terminating error behavior for one command
| Value | Engineering meaning |
|---|---|
Continue | Show the error and continue; normal default |
SilentlyContinue | Hide display, still record the non-terminating error in $Error |
Ignore | Hide and, for non-terminating errors, do not add it to $Error |
Stop | Escalate a non-terminating error so normal control flow stops and it can be caught |
Inquire | Ask interactively what to do; poor fit for unattended automation |
$missing = Join-Path ([System.IO.Path]::GetTempPath()) 'no-such-devops-file.txt'
Get-Item -LiteralPath $missing -ErrorAction SilentlyContinue
'continued'-ErrorAction when a specific operation requires different handling. Avoid making an entire script silent merely to make the console look clean.3. $ErrorActionPreference is a scope-wide policy, so change it deliberately
$ErrorActionPreference supplies the default behavior for commands in the current scope and child scopes unless a command overrides it with -ErrorAction. Changing it affects far more code than changing one invocation.
& {
$ErrorActionPreference = 'Stop'
try {
Get-Item -LiteralPath './missing-inside-scope'
}
catch {
'caught because the scope preference escalated the error'
}
}
# Outside the script block, the caller's preference is unchanged.
$ErrorActionPreferenceScoping the preference change reduces accidental impact on unrelated commands. In reusable functions, explicit local handling is usually easier to reason about than silently modifying global policy.
4. $Error is a history of ErrorRecord objects, newest first
$Error stores recent errors with index 0 representing the most recent record. It is useful evidence, but it is shared session state: do not assume that $Error[0] always belongs to a particular operation unless you deliberately captured the error.
$Error.Clear()
Get-Item -LiteralPath './missing-for-inspection' -ErrorAction SilentlyContinue
$errorRecord = $Error[0]
$errorRecord.GetType().FullName
$errorRecord.Exception.GetType().FullName
$errorRecord.CategoryInfo
$errorRecord.FullyQualifiedErrorIdThe object contains structured metadata: the exception, category, invocation context, target object, and identifiers. A production log can select useful fields without scraping the first rendered line.
5. Use -ErrorVariable when you need errors from one command
$commandErrors = @()
Get-Item -LiteralPath './missing-a','./missing-b' -ErrorAction Continue -ErrorVariable +commandErrors
$commandErrors | Select-Object FullyQualifiedErrorId,CategoryInfo,TargetObjectThe leading + appends to the variable rather than replacing it. This is clearer than trying to infer which entries in the session-wide $Error list belong to your command.
6. An ErrorRecord is machine-usable evidence, not just red text
| Property | What it helps answer |
|---|---|
Exception | What .NET/PowerShell exception object describes the failure? |
CategoryInfo | Was it object-not-found, invalid argument, permission denied, and so on? |
FullyQualifiedErrorId | What stable-ish error identity did the command provide? |
TargetObject | Which input/resource was associated with the failure? |
InvocationInfo | Where and how was the failing command invoked? |
ScriptStackTrace | What script call path led to the error? |
Not every provider fills every field equally, but the record is still a better foundation for diagnostics than string matching against formatted console text.
7. Create a safe, predictable filesystem error
A disposable missing path is a good teaching failure: no critical system resource is touched and the expected error is deterministic.
$root = Join-Path ([System.IO.Path]::GetTempPath()) 'ps-error-demo'
New-Item -ItemType Directory -Path $root -Force | Out-Null
$missing = Join-Path $root 'not-created.txt'
Get-Content -LiteralPath $missing -ErrorAction Continue
'PowerShell reached this statement'
Remove-Item -LiteralPath $root -Recurse -ForceThe same operation with -ErrorAction Stop becomes suitable for try/catch, which is the focus of the next lesson.
8. Suppressing display is not the same as handling failure
# Dangerous pattern: failure becomes easy to overlook.
$data = Get-Content -LiteralPath './configuration.json' -ErrorAction SilentlyContinue
# Better: choose an explicit fallback or stop.
if ($null -eq $data) {
throw 'Configuration could not be loaded.'
}Silencing an expected optional lookup can be legitimate. Silencing a required configuration load and continuing with partially initialized state is not. The engineering question is always: what state is safe after this operation fails?
9. Stop is a bridge from command errors to structured exception handling
try {
Get-Content -LiteralPath './missing-required-config.json' -ErrorAction Stop
'not reached'
}
catch {
[pscustomobject]@{
ErrorId = $_.FullyQualifiedErrorId
Category = $_.CategoryInfo.Category
Message = $_.Exception.Message
}
}Inside catch, $_ (or $PSItem) is the current ErrorRecord. Lesson 3 develops this into deliberate failure paths with cleanup and rethrowing.
10. Lab: inspect errors instead of only reading their display text
$root = Join-Path ([System.IO.Path]::GetTempPath()) 'ps-error-lab'
New-Item -ItemType Directory -Path $root -Force | Out-Null
$missing = Join-Path $root 'required.json'
$captured = @()
Get-Content -LiteralPath $missing -ErrorAction SilentlyContinue -ErrorVariable +captured
$captured | ForEach-Object {
[pscustomobject]@{
ExceptionType = $_.Exception.GetType().FullName
Category = $_.CategoryInfo.Category
Target = $_.TargetObject
ErrorId = $_.FullyQualifiedErrorId
}
}
Remove-Item -LiteralPath $root -Recurse -Force- Confirm that the command continues despite the missing file.
- Compare
SilentlyContinuewithIgnoreand observe whether the non-terminating error enters$Error. - Repeat with
-ErrorAction Stopinsidetry/catch. - Explain why a global
$ErrorActionPreference = SilentlyContinuewould make a large automation harder to trust.
11. Common mistakes and the underlying model
| Mistake | What actually happens | Better pattern |
|---|---|---|
| Assume every red message stops execution | Many cmdlet errors are non-terminating | Choose ErrorAction based on required control flow |
| Set global SilentlyContinue to “clean up” logs | Failures become hidden across unrelated commands | Handle expected errors narrowly |
| Parse the first error line | Rendered text loses structured metadata | Inspect ErrorRecord properties |
Use $Error[0] without isolating the operation | Another error may become newest | Use -ErrorVariable or catch the specific operation |
12. Knowledge check
Question 1. What does -ErrorAction Stop do to a non-terminating error?
Question 2. What is the main difference between SilentlyContinue and Ignore for a non-terminating error?
Question 3. Where is the most recent session error normally found?
$Error[0].Question 4. What object type should you inspect for PowerShell error metadata?
System.Management.Automation.ErrorRecord.Question 5. Why is globally suppressing errors dangerous?
13. Summary
PowerShell errors have control-flow behavior as well as display behavior. Non-terminating errors can report a problem and continue; -ErrorAction changes that policy for one command, while $ErrorActionPreference affects a broader scope. $Error, -ErrorVariable, and ErrorRecord properties provide structured evidence. Suppression is not handling: choose whether an error is optional, recoverable, or should become terminating, then encode that decision explicitly.
14. Further reading
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.