skip to content

Error Handling

Terminating versus non-terminating errors and the levers that control them: $ErrorActionPreference, -ErrorAction, try/catch/finally and -ErrorVariable. Interviewers ask because a script that cheerfully continues after a failed step is PowerShell's most dangerous default.

part ofCommand-line shellsoverview, primer and where to startread it →
on this pageshow

questions

5

In PowerShell, a Get-Item call inside a try block fails and prints a red error, but the catch block never runs and the script keeps going. Explain the difference between terminating and non-terminating errors, and how you make that call catchable.

level: middleimportance: must knowfreq 76%

answer

  1. two error kinds, not one
  2. red text is not an exception
  3. catch only sees the terminating kind
  4. one common parameter converts them
  5. -ErrorAction Stop on the call

basics

~20 s

Most cmdlet failures are non-terminating errors: they write a record to the error stream and execution continues, so try/catch never fires. Adding -ErrorAction Stop turns that call's errors into terminating ones, which a catch block handles.

solid answer

~50 s

PowerShell has two error kinds. A **terminating** error stops the current pipeline and unwinds until something catches it — `throw`, an exception from a .NET method, or a cmdlet that deliberately raises one. A **non-terminating** error is a report, not a halt: the cmdlet writes an `ErrorRecord` to the error stream, the host prints it in red, and the next statement runs. `try/catch` is exception machinery, so it reacts only to the terminating kind — which is exactly why you can see red text while `catch` stays silent. The fix is `-ErrorAction Stop` on that call, which converts that cmdlet's non-terminating errors into terminating ones. `$ErrorActionPreference = 'Stop'` does the same for a whole scope, but I prefer the per-call parameter: it is explicit and does not change behaviour for code I did not mean to affect. Be aware Stop also makes a multi-item cmdlet abort at the first failure.

code

powershell · 14 lines
powershell
# Non-terminating: the catch block is never entered
try {
    Get-Item 'C:\does-not-exist'
    Write-Output 'try block continued'
} catch {
    Write-Output 'not reached'
}

# Terminating: -ErrorAction Stop makes the same failure catchable
try {
    Get-Item 'C:\does-not-exist' -ErrorAction Stop
} catch {
    Write-Output "caught: $($_.Exception.Message)"
}

go deeper

for a junior

Recall that a red error message does not necessarily stop a PowerShell script, and that -ErrorAction Stop is what makes a failing cmdlet call reach a catch block.

for a middle

Explain the two error kinds, name $ErrorActionPreference and its Continue default, and describe precisely how -ErrorAction Stop converts one call's errors into terminating ones.

for a senior

Show judgment about where the lever goes: per-call -ErrorAction Stop inside try blocks rather than a blanket preference, and note that Stop makes a multi-item cmdlet abort at the first failure.

for a principal

Argue for a house convention — fail fast versus attempt-everything-and-report — and be explicit about what a Stop preference does not cover, such as external programs that merely exit non-zero.

## Two error kinds, not one Every confusing `try/catch` story in PowerShell starts with a split that most other shells and languages do not have. A **terminating error** stops the current pipeline immediately and unwinds the call stack until something handles it. `throw` raises one. An exception thrown by a .NET method you called is one. A cmdlet that hits a condition it cannot continue past raises one deliberately (internally through `ThrowTerminatingError`). A **non-terminating error** is a *report*. The cmdlet writes an `ErrorRecord` object to the error stream (stream 2), the host renders it in red, `$?` becomes `$false`, the record is appended to `$Error`, and execution continues with the next input item and the next statement. `Get-ChildItem` walking a large tree and hitting one unreadable directory is the canonical case: you want the other 999 directories, not an abort. Most cmdlet failures are non-terminating. That is the design, and it is why the default value of `$ErrorActionPreference` in a fresh session is `Continue`. ## Why catch is never entered `try/catch` is exception machinery: it only sees terminating errors. A non-terminating error unwinds nothing, so control never leaves the `try` block abnormally and the `catch` block is simply skipped. ```powershell try { Get-Item 'C:\does-not-exist' # non-terminating: prints red, keeps going 'still running' # this DOES print } catch { 'never printed' } ``` The red text makes it look like an exception fired. It did not. Candidates who have only ever seen exceptions in C# or Java assume that error output implies unwinding, and write `try/catch` wrappers that quietly handle nothing. ## The lever: -ErrorAction Stop `-ErrorAction` is a *common parameter*, available on every cmdlet and on advanced functions. Setting it to `Stop` converts that one call's non-terminating errors into terminating ones: ```powershell try { Get-Item 'C:\does-not-exist' -ErrorAction Stop } catch { "handled: $($_.Exception.Message)" } ``` The full set of values is worth knowing: - `Continue` — print the error, keep going. The default. - `SilentlyContinue` — keep going without printing, but the record is still added to `$Error` (and to `-ErrorVariable` if you asked for one). - `Ignore` — discard the error entirely; it is not recorded in `$Error`. Valid only as a `-ErrorAction` argument; assigning `Ignore` to `$ErrorActionPreference` is an error. - `Stop` — make it terminating. - `Inquire` — prompt the user. - `Break` — enter the debugger (PowerShell 7.x). ## The scope lever: $ErrorActionPreference `$ErrorActionPreference` is a preference variable applying to cmdlet calls that do not specify `-ErrorAction` themselves. It is scoped like any variable: setting it at the top of a script affects that script and code called from it, and it reverts when the scope ends — it does not permanently reconfigure the user's session. `-ErrorAction` on an individual call always wins over the preference. Many teams put `$ErrorActionPreference = 'Stop'` at the top of automation scripts to get fail-fast behaviour. That is a defensible convention, but understand what it does *not* cover: errors that are already terminating (unchanged), exceptions from .NET method calls (already terminating and always catchable), and external programs that merely exit non-zero — those are not error records at all. ## The cost of Stop on a multi-item cmdlet One consequence catches people out. A cmdlet given many inputs normally reports a non-terminating error per bad item and processes the rest. Under `-ErrorAction Stop` it aborts at the first failure, leaving the remaining items untouched: ```powershell # processes every reachable path, complains about the rest Get-Item $paths # stops dead at the first missing path Get-Item $paths -ErrorAction Stop ``` If you want every item attempted *and* a record of the failures, do not use `Stop` on the bulk call — either loop with a per-item `try/catch` and `-ErrorAction Stop` inside the loop, or keep going with `SilentlyContinue` and collect the records. ## finally `finally` runs whether or not an error occurred, and runs before the error continues propagating — the place for releasing a session, closing a file handle, or restoring a changed preference. A `try` needs only one of `catch` or `finally`; `try/finally` with no catch is a legitimate cleanup pattern that lets the error escape to a caller who knows what to do with it.

  • Does -ErrorAction Stop also affect an exception thrown by a .NET method call inside your script?
    No. `-ErrorAction` is a common parameter honoured by cmdlets and advanced functions; it means nothing to a .NET method call. But you do not need it there: a method that throws raises a terminating error already, so a surrounding `catch` handles it either way.
  • What is the practical difference between -ErrorAction SilentlyContinue and -ErrorAction Ignore?
    Both suppress the red output and continue. `SilentlyContinue` still records the `ErrorRecord` in `$Error`, so you can inspect it afterwards; `Ignore` discards it entirely, leaving nothing to audit. `Ignore` is also legal only as a parameter argument — assigning it to `$ErrorActionPreference` fails.
  • If you put $ErrorActionPreference = 'Stop' at the top of a script, does it leak into the user's session after the script ends?
    No. It is an ordinary variable subject to PowerShell's scoping, so the assignment lives in the script's scope and disappears when the script exits. It does apply to functions the script calls, unless they set their own value or the call passes an explicit `-ErrorAction`.

saying these in an interview costs you the question

  • Believes try/catch catches every PowerShell error
  • Says a red error message always stops the script
  • Wraps calls in try/catch but never adds -ErrorAction Stop
  • Thinks $ErrorActionPreference changes the machine permanently
  • Confuses -ErrorAction with -ErrorVariable

context

open as a page

In PowerShell, what is the difference between throw and Write-Error, and when would you choose each inside a function?

level: juniorimportance: should knowfreq 55%

basics

~10 s

throw raises a terminating error that unwinds to the nearest catch and stops the current scope. Write-Error emits a non-terminating error record on the error stream and execution continues with the next statement.

open as a page

In PowerShell, how do you catch one specific failure type in a catch block rather than everything, and what does the object in $_ actually contain?

level: middleimportance: should knowfreq 44%

basics

~20 s

Put a .NET exception type in brackets after the catch keyword, such as catch [System.IO.IOException], and order blocks from most specific to most general. Inside, $_ is an ErrorRecord holding the exception, target object, category and script stack trace.

open as a page

A PowerShell script loops over hundreds of inputs calling a cmdlet for each. It must not stop at the first failure, but at the end it must report exactly which ones failed. How do you capture those errors, and why is $Error not a good enough answer?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Pair -ErrorAction SilentlyContinue with -ErrorVariable on the call inside the loop, prefixing the variable name with a plus sign so each error record is appended rather than overwriting the last. $Error is session-wide, capped, and mixed with unrelated failures.

open as a page

A PowerShell script runs an external program such as git inside a try block. The program fails and prints its error text, but the catch block never runs and the script carries on. Why do external programs behave differently from cmdlets, and how should the script detect the failure?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

An external program is a separate process that reports failure with an exit code, not with a PowerShell error record, so try/catch and $ErrorActionPreference never see it. Check $LASTEXITCODE after the call and throw yourself.

open as a page