HBoPS #2: Error Handling and you!

OK, OK, I’m guilty at this one. I don’t always build proper error handling in to my scripts, but it’s something I’m working on!

Proper error handling is important… Knowing what happens matters! That is why we can use the Try{}Catch{} statements to deal with things that go wrong…

Setting Error Action Preference to Silently Continue

Consider the block below… It has error handling (the try/catch), but since the $ErrorActionPreference is set to SilentlyContinue, it won’t show an error. You might be left wondering why no information was returned, and no warning was given!

$ErrorActionPreference = 'SilentlyContinue'
try{
Get-item -Path C:\ThisDoesntExist\
}Catch{
write-warning "Item not found"
}

Output of this code:

Setting Error Action Preference to Continue (default)

Now the same code, but with the $ErrorActionPreference set to the default value (Continue) will produce something quite different!

$ErrorActionPreference = 'Continue'
try{
Get-item -Path C:\ThisDoesntExist\
}Catch{
write-warning "Item not found"
}

Output of this code:

As you can see, we’re now seeing an error! That’s great!

Terminating non-terminating errors

Although we see an error in the previous image, it’s not the action we specified in the Catch block. And that’s because get-item is not generating a terminating error. If we want the action in our catch block to be generated, we need to add an error action:

$ErrorActionPreference = 'Continue'
try{
Get-item -Path C:\ThisDoesntExist\ -ErrorAction Stop
}Catch{
write-warning "Item not found"
}

Output:

There she blows! We now get our catch statement activated and displaying our warning message..

Some people argue that setting $ErrorActionPreference to Stop at the beginning of your script make you write better code. It’s not something I have done ever. If you do go that route, remember to set the $ErrorActionPreference back to Continue at the end! That preference is a global preference, and you’d mess up someones day if you don’t 🙂

Returning error to the end-user

So we’ve been able to trigger our warning! But what was the exact error? With the following block of code we can show that to our end-user:

$ErrorActionPreference = 'Continue'
try{
Get-item -Path C:\ThisDoesntExist\ -ErrorAction Stop
}Catch{
write-warning "Item not found"
$Error[0].Exception.GetType().FullName
}

Output:

Specific Catch Conditions

Which brings us to the following item… We can write multiple catches, based on the specific error we expect to possibly happen:

 
$ErrorActionPreference = 'Continue'

try{
Get-item -Path C:\ThisDoesntExist\ -ErrorAction Stop
}Catch [System.Management.Automation.ItemNotFoundException]{
write-warning "Item not found"
$Error[0].Exception.GetType().FullName
}catch [System.Management.ManagementException]{
write-warning "Oops... Something went wrong"
}

Multiple catch blocks can exist within a Try/Catch, and you can scope them to a specific kind of error!

You can even end your try/catch with a simple, non-scoped catch. Think of it as a Default error message to display if none of the other error conditions match…

$ErrorActionPreference = 'Continue'
try{
Get-item -Path C:\ThisDoesntExist\ -ErrorAction Stop
}Catch [System.Management.Automation.ItemNotFoundException]{
write-warning "Item not found"
$Error[0].Exception.GetType().FullName
}Catch [System.Management.ManagementException]{
Write-warning "Oops… Something went wrong!"
}Catch{
Write-Warning "An unexpected error happened!"
Write-Warning $_
}

Exception information, and how to access it

Within a catch block you can access the current error using $_. I’ve used this in an example above, and it’s a pretty nifty way to give more information.

Standard error handling structure

So with all of the above information, the following “standard” error handling structure emerges:

 
Try{
<#
This block is where we are going to do something.
Our first action is going to be to set the error action preference to STOP, catching all those pesky non-terminating errors get picked up.
Secondly, the executing code will be added
Thirdly we report back
#>
# Setting the error action preference to catch non-terminating error
$ErrorActionPreference = 'Stop'

}Catch{
<#
This block is our final catch block.
Catches for speficic error should be added before this one in the following format:

Catch[error type to catch]{

}
#>

Write-warning ('Oops... Something went wrong. Looks like we encountered an unintended error of the type: ' + $_.Exception.GetType().FullName)
Write-warning $_

}Finally{
<#
Our Final block in the process is used for cleanup, in our case setting the error action preference back to 'Continue', since we don't want
to leave it at 'Stop'
#>
# Setting the error action preference back to the default value
$ErrorActionPreference = 'Continue'
}

A quick run through the code shows us that we split our handling up in 3 parts: Try, Catch, and Finally. Typically we try and catch every error we can think of (and test) in the catch blocks, but it is always good to leave a ‘catch all’ for those weird things you didn’t think of. And although I said never to change the error action preference setting, we are doing it here, so we don’t have to remember to add on an erroraction parameter to each cmdlet (work smarter). Anyway, we’re setting it back in our Finally block, which always runs.

Recommended reading

Index:
HBoPS #1: Use a proper editor!
HBoPS #2: Error handling and you!
HBoPS #3: Avoid using Write-Host (and save puppies!)
HBoPS #4: Variables, Parameters, and Battlestar Galactica
HBoPS #5: Reduce, Reuse, Recycle
HBoPS #6: Handling Credentials

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Close Menu
%d bloggers like this: