Error Handling

Learn PowerShell error handling with try/catch/finally, error types, ErrorAction, trap statements, and best practices for writing robust scripts.

📖 5 min read📅 2026-02-10Scripting

Understanding Errors in PowerShell

PowerShell has two types of errors:

Terminating Errors

Stop execution immediately. Thrown by throw, .NET exceptions, and cmdlets with -ErrorAction Stop.

Non-Terminating Errors

Report an error but continue execution. Most cmdlet errors are non-terminating by default.

# Non-terminating: continues to the next item
Get-Item "nonexistent.txt", "C:\Windows"
# Error for nonexistent.txt, then shows C:\Windows
 
# Terminating: stops everything
throw "This stops execution"

Try / Catch / Finally

The primary error handling mechanism:

try {
    # Code that might fail
    $content = Get-Content "nonexistent.txt" -ErrorAction Stop
    Write-Host "File content: $content"
}
catch {
    # Handle the error
    Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
}
finally {
    # Always runs (cleanup code)
    Write-Host "Operation complete"
}

Catching Specific Exceptions

try {
    $result = 10 / 0
}
catch [System.DivideByZeroException] {
    Write-Host "Cannot divide by zero!"
}
catch [System.IO.FileNotFoundException] {
    Write-Host "File not found!"
}
catch [System.UnauthorizedAccessException] {
    Write-Host "Access denied!"
}
catch {
    # Catch everything else
    Write-Host "Unexpected error: $($_.Exception.Message)"
}

Error Object Properties

try {
    Get-Item "nonexistent.txt" -ErrorAction Stop
}
catch {
    # The error record ($_) has many useful properties
    Write-Host "Message:    $($_.Exception.Message)"
    Write-Host "Type:       $($_.Exception.GetType().FullName)"
    Write-Host "Category:   $($_.CategoryInfo.Category)"
    Write-Host "Target:     $($_.TargetObject)"
    Write-Host "Script:     $($_.InvocationInfo.ScriptName)"
    Write-Host "Line:       $($_.InvocationInfo.ScriptLineNumber)"
    Write-Host "Command:    $($_.InvocationInfo.MyCommand)"
    Write-Host "Stack:      $($_.ScriptStackTrace)"
}

ErrorAction Parameter

Control how individual commands handle errors:

# Stop: Convert to terminating error (catchable)
Get-Item "nonexistent.txt" -ErrorAction Stop
 
# SilentlyContinue: Suppress error messages
Get-Item "nonexistent.txt" -ErrorAction SilentlyContinue
 
# Continue: Show error but keep going (default)
Get-Item "nonexistent.txt" -ErrorAction Continue
 
# Inquire: Ask the user what to do
Get-Item "nonexistent.txt" -ErrorAction Inquire
 
# Ignore: Like SilentlyContinue but doesn't add to $Error
Get-Item "nonexistent.txt" -ErrorAction Ignore

ErrorActionPreference

Set the default error behavior for the entire session or script:

# Make all errors terminating by default
$ErrorActionPreference = "Stop"
 
# Common in scripts:
$ErrorActionPreference = "Stop"
try {
    # Now ALL errors are catchable
    Get-Item "file1.txt"
    Get-Content "file2.txt"
    Set-Content "file3.txt" -Value "data"
}
catch {
    Write-Error "Something failed: $_"
}
finally {
    $ErrorActionPreference = "Continue"  # Reset
}

The $Error Variable

PowerShell keeps a history of all errors:

# Most recent error
$Error[0]
 
# All errors in session
$Error
 
# Number of errors
$Error.Count
 
# Clear error history
$Error.Clear()
 
# Check if last command succeeded
if ($?) {
    Write-Host "Last command succeeded"
}
else {
    Write-Host "Last command failed"
}

Throw — Creating Custom Errors

# Simple throw
throw "Something went wrong!"
 
# Throw with a specific exception type
throw [System.IO.FileNotFoundException]::new("Config file not found")
 
# Conditional throw
function Set-Age {
    param([int]$Age)
 
    if ($Age -lt 0 -or $Age -gt 150) {
        throw "Age must be between 0 and 150. Got: $Age"
    }
 
    Write-Host "Age set to $Age"
}
 
try {
    Set-Age -Age -5
}
catch {
    Write-Host "Validation error: $_" -ForegroundColor Red
}

Write-Error vs Throw

# Write-Error: Non-terminating (continues execution)
function Test-WriteError {
    Write-Error "Something went wrong"
    Write-Host "This still runs!"
}
 
# Throw: Terminating (stops execution)
function Test-Throw {
    throw "Something went wrong"
    Write-Host "This never runs!"
}
 
# Use Write-Error for warnings/non-critical errors
# Use throw for critical errors that should stop execution

Practical Error Handling Patterns

Pattern 1: Retry Logic

function Invoke-WithRetry {
    param(
        [scriptblock]$ScriptBlock,
        [int]$MaxRetries = 3,
        [int]$DelaySeconds = 5
    )
 
    $attempt = 0
    $lastError = $null
 
    while ($attempt -lt $MaxRetries) {
        $attempt++
        try {
            Write-Host "Attempt $attempt of $MaxRetries..."
            $result = & $ScriptBlock
            return $result
        }
        catch {
            $lastError = $_
            Write-Warning "Attempt $attempt failed: $($_.Exception.Message)"
            if ($attempt -lt $MaxRetries) {
                Write-Host "Retrying in $DelaySeconds seconds..."
                Start-Sleep -Seconds $DelaySeconds
            }
        }
    }
 
    throw "All $MaxRetries attempts failed. Last error: $lastError"
}
 
# Usage
Invoke-WithRetry -ScriptBlock {
    Invoke-WebRequest "https://api.example.com/data" -TimeoutSec 10
} -MaxRetries 3 -DelaySeconds 5

Pattern 2: Logging Errors

function Write-Log {
    param(
        [string]$Message,
        [ValidateSet("INFO", "WARN", "ERROR")]
        [string]$Level = "INFO"
    )
 
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$timestamp] [$Level] $Message"
 
    Add-Content -Path "script.log" -Value $logEntry
 
    switch ($Level) {
        "ERROR" { Write-Host $logEntry -ForegroundColor Red }
        "WARN"  { Write-Host $logEntry -ForegroundColor Yellow }
        default { Write-Host $logEntry }
    }
}
 
# Usage in error handling
try {
    Write-Log "Starting file processing" -Level INFO
    $data = Get-Content "data.csv" -ErrorAction Stop
    Write-Log "Processed $($data.Count) lines" -Level INFO
}
catch {
    Write-Log "Failed to process file: $($_.Exception.Message)" -Level ERROR
    Write-Log "Stack trace: $($_.ScriptStackTrace)" -Level ERROR
}

Pattern 3: Cleanup with Finally

$connection = $null
try {
    $connection = [System.Data.SqlClient.SqlConnection]::new($connectionString)
    $connection.Open()
 
    # Do database work...
    $command = $connection.CreateCommand()
    $command.CommandText = "SELECT * FROM Users"
    $reader = $command.ExecuteReader()
}
catch {
    Write-Error "Database error: $($_.Exception.Message)"
}
finally {
    # Always close the connection
    if ($connection -and $connection.State -eq 'Open') {
        $connection.Close()
        Write-Verbose "Database connection closed"
    }
}

Exercises

  1. Write a function that safely reads a file and returns $null with a warning if not found
  2. Create a script with retry logic that pings a server until it responds
  3. Build an error-logging function that writes to both console and log file
  4. Write a try/catch block that catches specific .NET exceptions differently
  5. Create a validation function that throws descriptive errors for invalid input

Next: File System Operations — learn to manage files and folders!