PowerShell Try-Catch-Finally error handling

eye-catch Other techs

While coding in PowerShell, error handling is necessary. Even though the code is small enough, we want to know why it fails and on which line the exception is thrown.

Sponsored links

Catch an error and write the reason

Let’s start with the most basic one. PowerShell supports try-catch-finally block. Look at the following code.

try {
    Write-Host "Try"
    NonsenseString
    Write-Host "DONE"
}
catch {
    # executed only when an error occurs
    Write-Host "Catch"  
}
finally {
    # always executed
    Write-Host "Final"
}
# Try
# Catch
# Final

The result doesn’t show DONE because an error is thrown on the third line. The lines written in catch block is executed only when an error occurs in try block. Then, finally block is always executed. finally block can be used, for example, when you want to close a file after reading it.

Sponsored links

How to get an error info

We want to know the error info when an error occurs.

I wrote the following statement to change the error message language to English because my system language is Japanese, and thus the error message is written also in Japanese.

[cultureinfo]::CurrentUICulture = 'en-US' 

PSItem or _

When an error occurs, the info is stored in $PSItem or $_. You can use them by your preference.

try {
    Write-Host "Try"
    NonsenseString
    Write-Host "DONE"
}
catch {
    Write-Host "Catch"

    Write-Host "`nMessage: " $PSItem
    Write-Host "`nMessage: " $_
# Message:  The term 'NonsenseString' is not recognized as the name of a cmdlet, function, 
# script file, or operable program. Check the spelling of the name, or if a path was included,
# verify that the path is correct and try again.
}

Both variables have the same info and we can get the error message here.

ScriptStackTrace

The message is not enough info if we want to know where the error occurs.

try {
    Write-Host "Try"
    NonsenseString
    Write-Host "DONE"
}
catch {
    Write-Host "Catch"
    Write-Host "`nScriptStackTrace: " $_.ScriptStackTrace
# ScriptStackTrace:  at <ScriptBlock>, C:\dir\powershell\scripts\ErrorHandling.ps1: line 18
# at <ScriptBlock>, <No file>: line 1
}

This is simple stack trace info but it could be hierarchical info if the code is more complicated.

Exception

This is actually thrown error.

try {
    Write-Host "Try"
    NonsenseString
    Write-Host "DONE"
}
catch {
    Write-Host "Catch"

    Write-Host "`nException: " $_.Exception
# Exception:  System.Management.Automation.CommandNotFoundException: The term 'NonsenseString' 
# is not recognized as the name of a cmdlet, function, script file, or operable program. 
# Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
# at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
# at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
# at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
# at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
}

It has Message, InnerException, StackTrace properties.

Handling a specific exception

There are some cases where we want to handle an error for a specific exception but ignore others. It is also possible by specifying the exception type.

Handling a single exception

The following example catches only System.IO.FileNotFoundException type.

try {
    throw [System.IO.FileNotFoundException] "File not found: C:/dir1/dir2/something.txt"
}
catch [System.IO.FileNotFoundException] {
    Write-Host "Catch1"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
finally {
    Write-Host "Final"
}
# Try
# Catch1
# File not found: C:/dir1/dir2/something.txt
#  at <ScriptBlock>, C:\dir\powershell\scripts\ErrorHandling.ps1: line 41
# at <ScriptBlock>, <No file>: line 1
# Final

Let’s throw a different exception.

try {
    throw [System.IO.IOException] "IOException occurred!!!"
}
catch [System.IO.FileNotFoundException] {
    Write-Host "Catch1"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
finally {
    Write-Host "Final"
}
# Try
# Final
# IOException occurred!!!
# 発生場所 C:\dir\powershell\scripts\ErrorHandling.ps1:42 文字:5
# +     throw [System.IO.IOException] "IOException occurred!!!"
# +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#     + CategoryInfo          : OperationStopped: (:) [], IOException
#     + FullyQualifiedErrorId : IOException occurred!!!

As you can see, Catch1 is not shown here but the error info was shown. The language is not English because this error was handled out of this script. The following code is executed at the top of the script but it is not applied for an unhandled exceptions.

[cultureinfo]::CurrentUICulture = 'en-US' 

Handling multiple exception types

How can we catch a different exception type? It’s easy. Write two catch blocks.

try {
    Write-Host "Try"
    throw [System.IO.FileNotFoundException] "File not found: C:/dir1/dir2/something.txt"
    # throw [System.IO.IOException] "IOException occurred!!!"
    Write-Host "DONE"
}
catch [System.IO.FileNotFoundException] {
    Write-Host "Catch1"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
catch [System.IO.IOException] {
    Write-Host "Catch2"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
finally {
    Write-Host "Final"
}

System.IO.FileNotFoundException is caught in the first catch block and System.IO.IOException is caught in the second catch block. You can do a different error handling there.

Same error handling for a different exception type

You might want to do the same error handling for a different exception type. It’s possible to specify multiple exception types in a catch block.

try {
    Write-Host "Try"
    throw [System.IO.FileNotFoundException] "File not found: C:/dir1/dir2/something.txt"
    # throw [System.IO.IOException] "IOException occurred!!!"
    Write-Host "DONE"
}
catch [System.IO.FileNotFoundException] , [System.IO.IOException] {
    Write-Host "type combined catch"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
finally {
    Write-Host "Final"
}
# Try
# type combined catch
# File not found: C:/dir1/dir2/something.txt
#  at <ScriptBlock>, C:\dir\powershell\scripts\ErrorHandling.ps1: line 41
# at <ScriptBlock>, <No file>: line 1
# Final

You can add multiple exception types by adding a type with a comma.

catch [System.IO.FileNotFoundException] , [System.IO.IOException] {

Catch a non-specified exception

If you want to do a special error handling for some exception types but don’t want to do anything for other exception types, you can write an additional catch block without exception type.

try {
    Write-Host "Try"
    throw [System.MissingMethodException] "MissingMethodException occurred!"
    Write-Host "DONE"
}
catch [System.IO.FileNotFoundException] , [System.IO.IOException] {
    Write-Host "type combined catch"
    Write-Host $_ "`n" $_.ScriptStackTrace
}
catch {
    Write-Host $_
    throw
}
finally {
    Write-Host "Final"
}
# Try
# MissingMethodException occurred!
# Final
# MissingMethodException occurred!
# 発生場所 C:\dir\powershell\scripts\ErrorHandling.ps1:43 文字:5
# +     throw [System.MissingMethodException] "MissingMethodException occ ...
# +     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#     + CategoryInfo          : OperationStopped: (:) [], MissingMethodException
#     + FullyQualifiedErrorId : MissingMethodException occurred!

If the error should be handled on the caller side, the error can be thrown again in the catch block. The example above doesn’t catch the re-thrown error.

If the error is caught, the error can be handled correctly.

try {
    try {
        throw [System.MissingMethodException] "MissingMethodException occurred!"
    }
    catch {
        Write-Host $_
        throw   
    }
    finally {
        Write-Host "Final"
    }
}
catch {
    Write-Host "====="
    Write-Host $_ "`n" $_.ScriptStackTrace
}
# Try
# MissingMethodException occurred!
# Final
# =====
# MissingMethodException occurred!
#  at <ScriptBlock>, C:\dir\powershell\scripts\ErrorHandling.ps1: line 43
# at <ScriptBlock>, <No file>: line 1

The point is using throw without any argument.

try {
    try {
        throw [System.MissingMethodException] "MissingMethodException occurred!"
    }
    catch {
        Write-Host $_
        # try to add argument
        throw [System.MissingMethodException] "MissingMethodException 222"  
    }
    finally {
        Write-Host "Final"
    }
}
catch {
    Write-Host "====="
    Write-Host $_ "`n" $_.ScriptStackTrace
}
# Try
# MissingMethodException occurred!
# Final
# =====
# MissingMethodException 222
#  at <ScriptBlock>, C:\dir\powershell\scripts\ErrorHandling.ps1: line 60
# at <ScriptBlock>, <No file>: line 1

The error message changed here. The outer catch block shows that the error place is the line where the error is thrown again.

Be careful when you need to re-throw the error.

Comments

Copied title and URL