Skip to content
February 27, 2026 β€’ Junior (1-3 years) How-To

PowerShell Error Handling Best Practices

Write robust PowerShell scripts with proper error handling. Try/Catch, -ErrorAction, and logging strategies.

PowerShell Error Handling Best Practices

Bad error handling makes scripts fragile. Good error handling makes them professional. Here’s how to write PowerShell that fails gracefully.

The Basics

-ErrorAction Parameter

# Different levels
Stop      # Throw exception (use with Try/Catch)
Continue  # Write to $error and continue (default)
SilentlyContinue # Suppress error message
Inquire   # Ask user what to do

# Examples
Get-Item C:\Missing -ErrorAction Stop
Get-Item C:\Missing -ErrorAction SilentlyContinue

Try/Catch/Finally

try {
    # Risky operation
    Get-Content "C:\important.txt" -ErrorAction Stop
    
    # Success path
    Write-Host "File found!"
}
catch {
    # What to do on failure
    Write-Host "Error: $($_.Exception.Message)"
    
    # Log the error
    $Error | Out-File "C:\Logs\errors.log" -Append
}
finally {
    # Always runs - cleanup
    Remove-Item $tempFile -ErrorAction SilentlyContinue
}

Types of Errors

Terminating vs Non-Terminating

# Terminating - stops execution
# Use -ErrorAction Stop to convert

# Non-terminating - continues
Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue

# Make terminating
$ErrorActionPreference = "Stop"

Catch Specific Errors

try {
    Get-ADUser "doesntexist"
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
    Write-Host "User not found"
}
catch [System.UnauthorizedAccessException] {
    Write-Host "Access denied"
}
catch {
    Write-Host "Unexpected error: $_"
}

Logging Errors

function Write-ErrorLog {
    param([string]$Message)
    
    $LogPath = "$env:TEMP\ScriptErrors.log"
    $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    
    "$Timestamp - $Message" | Out-File $LogPath -Append
}

# Usage
try {
    Invoke-RestMethod "https://api.example.com/data" -ErrorAction Stop
}
catch {
    Write-ErrorLog "API call failed: $($_.Exception.Message)"
    throw
}

Validation Patterns

# Pre-flight checks
function Install-App {
    param([string]$InstallerPath)
    
    # Validate input
    if (-not (Test-Path $InstallerPath)) {
        throw "Installer not found: $InstallerPath"
    }
    
    # Validate disk space
    $FreeSpace = (Get-PSDrive C).Free
    if ($FreeSpace -lt 1GB) {
        throw "Insufficient disk space"
    }
    
    # Proceed with install
}

Retry Logic

function Invoke-WithRetry {
    param(
        [scriptblock]$ScriptBlock,
        [int]$MaxAttempts = 3,
        [int]$DelaySeconds = 5
    )
    
    for ($i = 1; $i -le $MaxAttempts; $i++) {
        try {
            & $ScriptBlock -ErrorAction Stop
            return
        }
        catch {
            if ($i -eq $MaxAttempts) {
                throw "Failed after $MaxAttempts attempts: $_"
            }
            Write-Host "Attempt $i failed, retrying..."
            Start-Sleep -Seconds $DelaySeconds
        }
    }
}

# Usage
Invoke-WithRetry -ScriptBlock {
    Test-NetConnection -ComputerName "api.example.com" -Port 443
} -MaxAttempts 3 -DelaySeconds 5

$PSItem vs $_

# Both work the same
catch {
    $_.Exception.Message      # The error message
    $_.Exception.GetType()    # Exception type
    $_.ScriptStackTrace      # Where it happened
    $_.TargetObject           # What failed
}

Common Mistakes

❌ Bad:

Get-Item C:\file.txt
if ($?) { Write-Host "Success" }

βœ… Good:

try {
    Get-Item C:\file.txt -ErrorAction Stop
    Write-Host "Success"
}
catch {
    Write-Host "Failed"
}

Wrap-Up

Always assume things can fail. Use Try/Catch, log errors, and validate inputs. Your future self will thank you.

Questions? Drop them below!

Was this helpful?