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.
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.
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