Error handling: try/catch and -ErrorAction

PowerShell · Advanced · 12 min

Without understanding the two kinds of errors, try/catch silently fails to catch most cmdlet errors.

PowerShell has two kinds of errors:

  • Non-terminating errors, the default for most cmdlets: the error is reported (and added to $Error) but the command continues with the next item. try/catch does not see these.
  • Terminating errors: throw, .NET exceptions, and any error raised with -ErrorAction Stop. These stop the current statement and can be caught.

So the standard pattern is try { Some-Cmdlet -ErrorAction Stop } catch { ... }. Inside catch, $_ is the ErrorRecord: $_.Exception.Message, $_.Exception.GetType(), $_.TargetObject, $_.InvocationInfo.ScriptLineNumber. You can catch specific exception types first and a general catch last. finally always runs, for cleanup.

-ErrorAction (common parameter) per call: Stop, Continue (default), SilentlyContinue, Ignore. $ErrorActionPreference = 'Stop' at the top of a script makes all cmdlet errors terminating, which is the PowerShell equivalent of bash's set -e. -ErrorVariable errs collects a call's errors into a variable.

Native programs (git, docker) do not throw; check $LASTEXITCODE after them, or in 7.4+ set $PSNativeCommandUseErrorActionPreference = $true so a non-zero exit becomes an error. $? tells whether the last command succeeded.

Example

Get-Item /nope -ErrorAction SilentlyContinue   # non-terminating, hidden
$?                                             # False
try {
  Get-Item /nope -ErrorAction Stop             # now it is terminating
  'not reached'
} catch [System.Management.Automation.ItemNotFoundException] {
  "missing: $($_.TargetObject)"                # missing: /nope
} catch {
  "other error: $($_.Exception.Message)"
} finally {
  'cleanup always runs'                        # cleanup always runs
}
Get-Item /nope, $HOME -ErrorVariable errs -ErrorAction SilentlyContinue | Out-Null
$errs.Count                                    # 1   (the second path worked)
try { throw 'custom failure' } catch { "caught: $($_.Exception.Message)" }   # caught: custom failure
$zero = 0
try { 1 / $zero } catch [System.DivideByZeroException] { 'division by zero' }   # division by zero
$ErrorActionPreference = 'Stop'               # script-wide: every error terminates

Remember

  • try/catch only sees terminating errors; add -ErrorAction Stop
  • $_ in catch is the ErrorRecord; finally always runs
  • Check $LASTEXITCODE after native programs

Watch out for

  • Wrapping a cmdlet in try/catch without -ErrorAction Stop and wondering why catch never runs.

Check yourself

  1. `try { Remove-Item missing.txt } catch { 'oops' }` prints an error but not 'oops'. Why?
  2. How do you detect that `git push` failed in a script?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally

Error handling: try/catch and -ErrorAction · PowerShell · Topolearn