Try Catch in PowerShell (With Finally and Examples)

In PowerShell, try/catch runs the code in try and jumps to catch when a terminating error happens. For cmdlets, add -ErrorAction Stop so their errors reach the catch block:

try {
    $orders = Import-Csv -Path C:\psfaqs\Try\orders-2026-09.csv -ErrorAction Stop
    "Loaded $($orders.Count) orders"
}
catch {
    "Failed. $($_.Exception.Message)"
}

Output:

Failed. Could not find file 'C:\psfaqs\Try\orders-2026-09.csv'.
PowerShell try catch with -ErrorAction Stop
The missing file lands in catch, and $_ holds the error (PowerShell 7)

Inside catch, $_ is the error record, and $_.Exception.Message is the readable message. Below I explain why catch sometimes doesn’t run, catch specific error types, clean up with finally and throw your own errors.

Why catch doesn’t run without -ErrorAction Stop

Most cmdlet errors are non-terminating. PowerShell reports them and carries on, so try never notices:

$ran = 'no'
try { Get-Content -Path C:\psfaqs\Try\missing.csv 2>$null } catch { $ran = 'yes' }
"Without -ErrorAction Stop, catch ran: $ran"

$ran = 'no'
try { Get-Content -Path C:\psfaqs\Try\missing.csv -ErrorAction Stop } catch { $ran = 'yes' }
"With -ErrorAction Stop, catch ran:    $ran"

Output:

Without -ErrorAction Stop, catch ran: no
With -ErrorAction Stop, catch ran:    yes

-ErrorAction Stop turns that one error into a terminating error. To do it for a whole script, set $ErrorActionPreference = 'Stop' at the top.

Catch specific error types

Add several catch blocks, each with an exception type. PowerShell uses the first one that matches, and a plain catch at the end handles the rest:

$tests = @(
    { Get-Content -Path C:\psfaqs\Try\missing.csv -ErrorAction Stop }
    { [int]'ten' }
    { $count = 0; 150 / $count }
)
foreach ($test in $tests) {
    try { & $test }
    catch [System.Management.Automation.ItemNotFoundException] { "File not found:  $($_.Exception.GetType().Name)" }
    catch [System.Management.Automation.PSInvalidCastException] { "Bad number:      $($_.Exception.GetType().Name)" }
    catch [System.DivideByZeroException] { "Divide by zero:  $($_.Exception.GetType().Name)" }
    catch { "Something else:  $($_.Exception.GetType().FullName)" }
}

Output:

File not found:  ItemNotFoundException
Bad number:      PSInvalidCastException
Divide by zero:  DivideByZeroException
PowerShell catch specific exception types
Three different errors land in three different catch blocks (Windows PowerShell 5.1)

To find the type name for your own catch, print $_.Exception.GetType().FullName in a plain catch first.

Use finally for cleanup

finally runs whether try succeeded or failed. It’s the right place to close connections, delete temp files or stop a timer:

foreach ($file in 'orders.csv', 'missing.csv') {
    try {
        $rows = Import-Csv -Path "C:\psfaqs\Try\$file" -ErrorAction Stop
        "$file loaded $($rows.Count) rows"
    }
    catch { "$file failed" }
    finally { "  Cleanup ran for $file" }
}

Output:

orders.csv loaded 2 rows
  Cleanup ran for orders.csv
missing.csv failed
  Cleanup ran for missing.csv

finally even runs when you press Ctrl+C during try. Microsoft covers the details in about_Try_Catch_Finally.

Get error details inside catch

The error record carries much more than the message. These are the properties I log most often:

try {
    Get-Content -Path C:\psfaqs\Try\missing.csv -ErrorAction Stop
}
catch {
    "Type:     $($_.Exception.GetType().FullName)"
    "Category: $($_.CategoryInfo.Category)"
    "Error ID: $($_.FullyQualifiedErrorId)"
    "Line:     $($_.InvocationInfo.ScriptLineNumber)"
}

Output:

Type:     System.Management.Automation.ItemNotFoundException
Category: ObjectNotFound
Error ID: PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Line:     2
PowerShell catch error details from the ErrorRecord
Type, category, error ID and line number from $_ (PowerShell 7)

Every error also lands in $Error, newest first. PowerShell error variables covers $Error and -ErrorVariable.

Throw your own errors

Use throw when your script finds a problem PowerShell wouldn’t flag, such as too few rows. The caller catches it like any other error:

function Import-Orders {
    param([string]$Path)
    $orders = @(Import-Csv -Path $Path -ErrorAction Stop)
    if ($orders.Count -lt 3) { throw "Only $($orders.Count) orders in $Path. Expected at least 3." }
    $orders
}

try { Import-Orders -Path C:\psfaqs\Try\orders.csv }
catch { "Failed. $($_.Exception.Message)" }

Output:

Failed. Only 2 orders in C:\psfaqs\Try\orders.csv. Expected at least 3.

Inside a catch block, a bare throw passes the same error up to the caller. Throwing exceptions with a message has more patterns.

Frequently Asked Questions

How do I use try catch in PowerShell?

Put the risky code in try { } and the handling code in catch { }. Add -ErrorAction Stop to cmdlets inside try.

Why is my PowerShell catch block not working?

The error is non-terminating. Add -ErrorAction Stop to the cmdlet, or set $ErrorActionPreference = ‘Stop’.

How do I get the error message in catch?

Use $_.Exception.Message. $_ is the full error record.

Can I catch different errors separately?

Yes. Add catch blocks with types, such as catch [System.Management.Automation.ItemNotFoundException], then a plain catch.

When does the finally block run?

Always: after try succeeds, after catch handles an error and even when you press Ctrl+C. See Microsoft’s about_CommonParameters.

More on handling errors: