Error handling
So far, you've seen how adding parameters and flow-control constructs can make your scripts flexible and safer to use. But sometimes, you'll get errors in your scripts. You need a way to handle those errors.
Here are some factors to consider:
How to handle the error. Sometimes you get errors you can recover from, and sometimes it's better to stop the script. It's important to think about the kinds of errors that can happen and how to best manage them.
How severe the error is. There are various kinds of error messages. Some are more like warnings to the user that something isn't OK. Some are more severe, and the user really needs to pay attention. Your error-handling approach depends on the type of error. The approach could be anything from presenting a message to raising the severity level and potentially stopping the script.
Errors
A cmdlet or function, for example, might generate many types of errors. We recommend that you write code to manage each type of error that might occur, and that you manage them appropriately given the type. For example, say you're trying to write to a file. You might get various types of errors depending on what's wrong. If you're not allowed to write to the file, you might get one type of error. If the file doesn't exist, you might get another type of error, and so on.
There are two types of errors you can get when you run PowerShell:
Terminating error. An error of this type will stop execution on the row where the error occurred. You can handle this kind of error using either
Try-Catch
orTrap
. If the error isn't handled, the script will quit at that point and no statements will run.Note
The
Trap
construct is outside the scope of this module. If you're interested, see About Trap.Non-terminating error. This type of error will notify the user that something is wrong, but the script will continue. You can upgrade this type of error to a terminating error.
Managing errors by using Try/Catch/Finally
You can think of a terminating error as an unexpected error. These errors are severe. When you deal with one, you should consider what type of error it is and what to do about it.
There are three related constructs that can help you manage this type of error:
Try
. You'll use aTry
block to encapsulate one or more statements. You'll place the code that you want to run — for example, code that writes to a data source — inside braces. ATry
must have at least oneCatch
orFinally
block. Here's how it looks:Try { # Statement. For example, call a command. # Another statement. For example, assign a variable. }
Catch
. You'll use this keyword to catch or manage an error when it occurs. You'll then inspect the exception object to understand what type of error occurred, where it occurred, and whether the script can recover. ACatch
follows immediately after aTry
. You can include more than oneCatch
— one for each type of error — if you want. Here's an example:Try { # Do something with a file. } Catch [System.IO.IOException] { Write-Host "Something went wrong" } Catch { # Catch all. It's not an IOException but something else. }
The script tries to run a command that does some I/O work. The first
Catch
catches a specific type of error:[System.IO.IOException]
. The lastCatch
catches anything that's not a[System.IO.IOException]
.Finally
. The statements in this block will run regardless of whether anything goes wrong. You probably won't use this block much, but it can be useful for cleaning up resources, for example. To use it, add it as the last block:Try { # Do something with a file. } Catch [System.IO.IOException] { Write-Host "Something went wrong" } Catch { # Catch all. It's not an IOException but something else. } Finally { # Clean up resources. }
Inspecting errors
We've talked about exception objects in the context of catching errors. You can use these objects to inspect what went wrong and take appropriate measures. An exception object contains:
A message. The message tells you in a few words what went wrong.
The stacktrace. The stacktrace tells you which statements ran before the error. Imagine you have a call to function A, followed by B, followed by C. The script stops responding at C. The stacktrace will show that chain of calls.
The offending row. The exception object also tells you which row the script was running when the error occurred. This information can help you debug your code.
So how do you inspect an exception object? There's a built-in variable, $_
, that has an exception
property. To get the error message, for example, you would use $_.exception.message
. In code, it might look like this:
Try {
# Do something with a file.
} Catch [System.IO.IOException] {
Write-Host "Something IO went wrong: $($_.exception.message)"
} Catch {
Write-Host "Something else went wrong: $($_.exception.message)"
}
Raising errors
In some situations, you might want to cause an error:
Non-terminating errors. For this type of error, PowerShell just notifies you that something went wrong, by using the
Write-Error
cmdlet, for example. The script continues to run. That might not be the behavior you want. To raise the severity of the error, you can use a parameter like-ErrorAction
to cause an error that can be caught withTry/Catch
, like so:Try { Get-Content './file.txt' -ErrorAction Stop } Catch { Write-Error "File can't be found" }
By using the
-ErrorAction
parameter and the valueStop
, you can cause an error thatTry/Catch
can catch.Business rules. You might have a situation where the code doesn't actually stop responding, but you want it to for business reasons. Imagine you're sanitizing input and you check whether a parameter is a path. A business requirement might specify only certain paths are allowed, or the path needs to look a certain way. If the checks fail, it makes sense to throw an error. In a situation like this, you can use a
Throw
block:Try { If ($Path -eq './forbidden') { Throw "Path not allowed" } # Carry on. } Catch { Write-Error "$($_.exception.message)" # Path not allowed. }
Note
In general, don't use
Throw
for parameter validation. Use validation attributes instead. If you can't make your code work with these attributes, aThrow
might be OK.