PowerShell Error Handling

Error handling is the practice of anticipating, detecting, and responding to problems in a script. Without error handling, a single unexpected failure can crash an entire script, leaving systems in an incomplete or broken state. Proper error handling makes scripts robust, safe, and informative even when things go wrong.

Types of Errors in PowerShell

Error TypeDescriptionScript Behavior
Terminating ErrorStops execution immediatelyScript halts unless caught with try-catch
Non-Terminating ErrorDisplays an error but continuesScript keeps running by default

# Non-terminating error – script continues
Get-Item "C:\DoesNotExist\file.txt"
Write-Host "This still runs after the error"

# Terminating error – script stops (e.g., syntax error or thrown exception)
throw "Critical failure"
Write-Host "This line never runs"

$ErrorActionPreference

$ErrorActionPreference controls how PowerShell responds to non-terminating errors. Setting it to "Stop" converts non-terminating errors into terminating ones, making them catchable.

ValueBehavior
ContinueShow error and keep running (default)
StopTreat all errors as terminating — halts script
SilentlyContinueSuppress error messages and keep running
InquireAsk the user what to do after each error
IgnoreIgnore error completely — nothing recorded in $Error

# Set globally for the script
$ErrorActionPreference = "Stop"

# Override for a single cmdlet
Get-Item "C:\missing.txt" -ErrorAction SilentlyContinue
Get-Item "C:\missing.txt" -ErrorAction Stop

try-catch-finally

The try-catch-finally block is the standard structured error handling pattern.

  try {
      # Code that might fail
  }
  catch {
      # Runs only if an error occurs in try
  }
  finally {
      # ALWAYS runs, regardless of error or success
  }

Basic try-catch


try {
    $file = Get-Item "C:\Reports\missing.csv" -ErrorAction Stop
    Write-Host "File found: $($file.FullName)"
}
catch {
    Write-Host "Error: Could not find the file." -ForegroundColor Red
    Write-Host "Details: $($_.Exception.Message)"
}

Output:


Error: Could not find the file.
Details: Cannot find path 'C:\Reports\missing.csv' because it does not exist.

try-catch-finally


try {
    Write-Host "Opening database connection..."
    # Simulate connection failure
    throw "Database server unreachable"

    Write-Host "Connection established."
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
}
finally {
    Write-Host "Cleanup complete. Resources released." -ForegroundColor Yellow
}

Output:


Opening database connection...
Connection failed: Database server unreachable
Cleanup complete. Resources released.

Catching Specific Exception Types

Multiple catch blocks can target specific exception types — handling each kind of error differently.


try {
    $content = Get-Content -Path "C:\Config\settings.xml" -ErrorAction Stop
}
catch [System.IO.FileNotFoundException] {
    Write-Host "File does not exist. Creating default config..." -ForegroundColor Yellow
    Set-Content -Path "C:\Config\settings.xml" -Value "dev"
}
catch [System.UnauthorizedAccessException] {
    Write-Host "Permission denied. Run as Administrator." -ForegroundColor Red
}
catch {
    Write-Host "Unexpected error: $($_.Exception.Message)" -ForegroundColor Red
}

The $_ Error Object Inside catch

Inside a catch block, $_ is the error object. It holds full details about what went wrong.


try {
    1 / 0    # Division by zero
}
catch {
    Write-Host "Exception Type:    $($_.Exception.GetType().FullName)"
    Write-Host "Message:           $($_.Exception.Message)"
    Write-Host "Script Line:       $($_.InvocationInfo.ScriptLineNumber)"
    Write-Host "Command:           $($_.InvocationInfo.Line.Trim())"
}

Output:


Exception Type:    System.DivideByZeroException
Message:           Attempted to divide by zero.
Script Line:       2
Command:           1 / 0

throw – Raise a Custom Error

Use throw to deliberately raise an error with a custom message.


function Set-UserAge {
    param ([int]$Age)

    if ($Age -lt 1 -or $Age -gt 120) {
        throw "Invalid age: $Age. Must be between 1 and 120."
    }

    Write-Host "Age set to: $Age"
}

try {
    Set-UserAge -Age 150
}
catch {
    Write-Host "Input error: $($_.Exception.Message)" -ForegroundColor Red
}

Output:


Input error: Invalid age: 150. Must be between 1 and 120.

$Error Variable

PowerShell stores all errors from the current session in the automatic $Error variable. $Error[0] is the most recent error.


# Generate an error
Get-Item "C:\missing.txt" -ErrorAction SilentlyContinue

# Inspect the last error
Write-Host "Last error: $($Error[0].Exception.Message)"

# Show all errors this session
$Error | ForEach-Object { Write-Host $_.Exception.Message }

# Clear the error list
$Error.Clear()

ErrorVariable – Capture Errors from a Single Cmdlet


Get-Item "C:\missing.txt" -ErrorAction SilentlyContinue -ErrorVariable myError

if ($myError) {
    Write-Host "Caught error: $($myError[0].Exception.Message)"
}

Trap – Legacy Error Handling

trap is an older error handling mechanism. It intercepts errors in a script block. Modern scripts use try-catch instead, but trap appears in older code.


trap {
    Write-Host "Trapped error: $($_.Exception.Message)"
    continue    # Continue execution after the error
}

Get-Item "C:\missing.txt" -ErrorAction Stop
Write-Host "Script continues after trap"

Real-World Example – Robust File Processing Script


function Process-LogFile {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$FilePath
    )

    $logPath   = "C:\Logs\process_errors.log"
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

    try {
        Write-Verbose "Checking if file exists: $FilePath"

        if (-not (Test-Path $FilePath)) {
            throw [System.IO.FileNotFoundException] "File not found: $FilePath"
        }

        $lines = Get-Content -Path $FilePath -ErrorAction Stop
        Write-Host "Processing $($lines.Count) lines from $FilePath"

        foreach ($line in $lines) {
            # Process each line
            Write-Verbose "Processing: $line"
        }

        Write-Host "Processing complete." -ForegroundColor Green

    }
    catch [System.IO.FileNotFoundException] {
        $msg = "[$timestamp] FILE NOT FOUND: $FilePath"
        Add-Content -Path $logPath -Value $msg
        Write-Host $msg -ForegroundColor Yellow
    }
    catch [System.UnauthorizedAccessException] {
        $msg = "[$timestamp] ACCESS DENIED: $FilePath"
        Add-Content -Path $logPath -Value $msg
        Write-Host $msg -ForegroundColor Red
    }
    catch {
        $msg = "[$timestamp] UNEXPECTED ERROR: $($_.Exception.Message)"
        Add-Content -Path $logPath -Value $msg
        Write-Host $msg -ForegroundColor Red
    }
    finally {
        Write-Verbose "Function complete for: $FilePath"
    }
}

Process-LogFile -FilePath "C:\Logs\app.log" -Verbose
Process-LogFile -FilePath "C:\Logs\missing.log"

Summary

Error handling in PowerShell uses the try-catch-finally pattern as its backbone. The try block contains potentially failing code. The catch block handles specific or general errors gracefully. The finally block always runs for cleanup. $ErrorActionPreference and the per-cmdlet -ErrorAction parameter control how errors surface. The throw statement raises custom errors inside validation logic. The $Error variable and -ErrorVariable parameter capture errors for inspection and logging. Well-structured error handling is what separates basic scripts from production-quality automation.

Leave a Comment