When you’re writing PowerShell scripts, whether automating a common task or building a complex tool, errors are inevitable. Files might be missing, services might fail to start, or a network might be down. To ensure your script runs gracefully, handles unexpected issues, and provides helpful troubleshooting information, you need error-handling.

And building upon my recent script-troubleshooting post about Strict Mode, the good news is PowerShell provides a perfect tool to leverage for error-handling: this is where the try/catch (and optionally finally) blocks come into play.

In this post, I’ll explore how try/catch works in PowerShell, show you why it’s beneficial, and give you practical examples so you can apply this concept right away.

What Are Try and Catch Blocks?

At their core, try and catch blocks help you separate the “normal” flow of your script from the “error handling” logic. By wrapping a piece of code in a try block, you’re effectively saying, “Run this code, and if something goes wrong, handle it in the catch block.” This is similar to what you might see in other programming languages, but PowerShell’s adaptation makes it very natural for system administrators and script authors. • try block: Contains the code you want to monitor for errors. • catch block: Executes only if an error occurs within the try block.

Here’s a simple conceptual template:

1
2
3
4
5
6
try {
    # Code that might fail
}
catch {
    # Code that runs if an error occurs in the try block
}

Why Use Try/Catch Instead of Just Checking $Error?

PowerShell automatically stores errors in the $Error variable, so you might wonder: why not just check $Error after each command? While that’s possible, it’s not as clean, and it’s easy to forget or miss handling certain failures. Using try/catch provides a structured and intentional approach to handling errors. This approach is more maintainable and makes it clear to anyone reading your code how you intended to handle specific problems.

Key Benefits Include:

  1. Better Script Reliability: Your script won’t abruptly stop or fail silently; it can handle problems gracefully.
  2. Clearer Logic: Separating error handling code from main logic makes it easier to read and maintain your scripts.
  3. Improved Troubleshooting: You can provide meaningful error messages or perform recovery actions automatically when something goes wrong.
  4. Finer Control Over Errors: You can catch specific error types and handle them differently—useful when you anticipate certain known issues.

Turning Off “Silent Errors”:

By default, some PowerShell commands may produce “non-terminating” errors, which do not trigger a catch block. To ensure that you can catch these kinds of errors, you’ll need to tell PowerShell how to treat them. The $ErrorActionPreference variable or the -ErrorAction parameter on commands can escalate non-terminating errors into terminating errors, enabling catch to do its job.

For instance:

1
2
3
4
5
6
7
8
9
# Make all errors terminating within this scope
$ErrorActionPreference = 'Stop'

try {
    Get-ChildItem "C:\NonExistentPath" # This would normally produce a non-terminating error
}
catch {
    Write-Host "An error occurred: $($_.Exception.Message)"
}

In the above example, changing $ErrorActionPreference to ‘Stop’ ensures the Get-ChildItem command’s failure will be caught by the catch block, rather than just reported and then ignored.

Detailed Example:

Suppose you are automating a file backup process. If the source directory doesn’t exist or you hit a permissions issue, you don’t want your entire script to continue blindly. Instead, you want to handle that gracefully.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$ErrorActionPreference = 'Stop' # Ensure errors stop execution in try block

$sourcePath = "C:\SourceData"
$backupPath = "C:\BackupData"

try {
    # Confirm that the source directory exists
    if (-not (Test-Path $sourcePath)) {
        throw "Source directory does not exist."
    }

    # Attempt to copy the files
    Copy-Item -Path $sourcePath\* -Destination $backupPath -Recurse
    Write-Host "Backup successful!"
}
catch {
    Write-Host "Backup failed. Reason: $($_.Exception.Message)"
    # Optional: Add steps to alert an admin or log the issue
    # For example:
    # Send-MailMessage -SmtpServer 'mail.example.com' -To 'admin@example.com' -From 'script@example.com' -Subject 'Backup Failed' -Body "The backup operation failed: $($_.Exception.Message)"
}

In this scenario, if the backup fails for any reason (no source directory, no permission, disk full, etc.), the catch block runs and displays a helpful message. You could add more sophisticated error handling, like writing detailed logs or sending an email alert.

Catching Specific Exceptions:

PowerShell allows you to be even more granular. If you know certain commands might throw specific exceptions, you can catch them individually using this syntax:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
try {
    # Some code that might throw different types of errors
}
catch [System.IO.IOException] {
    Write-Host "A file I/O error occurred: $($_.Exception.Message)"
}
catch [UnauthorizedAccessException] {
    Write-Host "You do not have permission to access that resource."
}
catch {
    Write-Host "An unexpected error occurred: $($_.Exception.Message)"
}

By specifying exception types, you can tailor your response based on the kind of error encountered. Maybe I/O issues trigger a retry, while a permissions issue alerts the admin directly.

Including a Finally Block:

Optionally, you can add a finally block that runs regardless of whether an error was thrown. This is useful for cleanup tasks, like closing a database connection or removing temporary files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
try {
    # Code that might fail
}
catch {
    # Handle the error
}
finally {
    # This code runs no matter what (success or failure)
    Remove-Item "C:\Temp\Staging" -Force -Recurse
}

Using finally ensures that certain cleanup routines always execute, maintaining a consistent and predictable script environment.

Practical Tips for Beginners:

  1. Start Simple: Wrap a single critical command in a try/catch block and test it. Once comfortable, expand to larger sections of code.
  2. Use Meaningful Error Messages: Don’t just say “An error occurred.” Describe what might have gone wrong and what steps to take next.
  3. Keep Logs: Logging errors to a file or logging service makes long-term troubleshooting easier.
  4. Check $ErrorActionPreference : If you’re not seeing catch blocks trigger, ensure you’ve set $ErrorActionPreference = ‘Stop’ or use -ErrorAction Stop on the individual commands.

Wrap-up:

Try/catch blocks in PowerShell give you more reliable and understandable error handling. Rather than letting your script stumble over unforeseen problems, you can anticipate failures, provide useful feedback, and keep your code running smoothly.

By applying try/catch to your scripting tasks, you’ll find it easier to create robust, maintainable, and user-friendly PowerShell solutions.

I hope you make a start with trying try/catch today {pun-intended}, and when you do: just watch your PowerShell scripts become more professional, reliable, and easier to troubleshoot!

Happy scripting…