PowerShell How-To
All Errors in PowerShell Are Not Created Equal
Here's how to identify and fix terminating and non-terminating issues that might pop up.
- By Adam Bertram
- 07/28/2016
We all can't write perfect PowerShell scripts, unfortunately. Because we're fallible, we scripters sometimes see the dreaded red text on the blue background indicating something went wrong.
The red text represents some kind of error that occurred in your code. More precisely, it either represents a terminating error or a non-terminating error, and each has a unique behavior.
Errors in PowerShell can be classified by level of severity. If, for example, your code encounters a major problem that will make a difference on the rest of the code executing successfully, this is more severe than a simple "hiccup" in your script. Perhaps a script needs to enumerate some files but can't even see the folder to begin. This would be more severe than a problem reading only one of those files. In the first case, you'd want your script to terminate and let you know a problem occurred vs., in the second case, to simply notify you a problem occurred and continue on its way. This is the difference between a terminating and a non-terminating error.
Terminating Errors
Terminating errors completely stop script execution. As an example, let's say you have a script that creates a folder on a file share, adds some files to it on a couple different servers.
$serverNames = 'MEMBERSRV2','MEMBERSRV1'
$serverNames.foreach({
$serverName = $_
mkdir "\\$serverName\c$\FileShare"
@('file1.txt','file2.txt').foreach({
Write-Verbose –Message "Adding file $_ to $serverName"
Add-Content -Path "\\$serverName\c$\FileShare\$_" -Value ''
})
})
We can run this and it does as we expect when all servers are available. No errors are thrown. However, what happens when we run this when a server name that's not available is in the list?
Notice that I didn't receive any other verbose output which meant that this failed on the first server and stopped. Perhaps this was doing a large number of servers and you'd prefer it to just skip that server and continue? You could either add a check before you attempt to perform the folder creation step or you could add a try/catch block.
A try/catch block is a great way to handle terminating errors like this. A terminating error always throws an exception. When in a try/catch block, the catch block will always "catch" this exception allowing you to do something with it.
$serverNames = 'MEMBERSRV2','MEMBERSRV1'
$serverNames.foreach({
try {
$serverName = $_
mkdir "\\$serverName\c$\FileShare"
@('file1.txt','file2.txt').foreach({
Write-Verbose –Message "Adding file $_ to $serverName"
Add-Content -Path "\\$serverName\c$\FileShare\$_" -Value ''
})
} catch {
$_.Exception.Message
}
})
We now no longer received any nasty error text but rather just the error message instead. Our other server also processed as expected. This was because mkdir threw a terminating error but since a try/catch block was available, it was thrown in there rather than killing the entire script. Since the try/catch block was inside of the foreach block, the foreach loop continued to process the other elements.
Non-Terminating Errors
Non-terminating errors are less serious errors that write to the error stream and do not stop script execution. If you've ever used the Write-Error function, you've created a non-terminating error before. Using our previous example, I've decided that when my script can't create a folder for some reason to not treat it as a terminating error but rather a non-terminating one. I can do this by using the Write-Error cmdlet inside of the catch block.
Instead of $_.Exception.Message being inside of the catch block, I'll instead use Write-Error to output an error with that message.
Write-Error –Message $_.Exception.Message
You can now see we still received that "network path was not found" message but the script continued to function. I've essentially "converted" that terminating error into a non-terminating one.
Converting Non-Terminating Errors
to Terminating In the last example, I essentially converted a terminating error to a non-terminating one. Perhaps I want to go the other way and turn a non-terminating error into a terminating one. I can do that on a global scale or on a per-command call basis.
If I decided I want all errors to be terminating, I can set the $ErrorActionPreference variable to Stop. This will halt script execution for all errors seen. If I didn't necessarily want to stop script execution for all errors but for certain commands. For this, I can use the ErrorAction parameter on all cmdlets and advanced functions. On any cmdlet or advanced function (no basic functions), I can simply specify an ErrorAction of Stop and if that command throws a non-terminating error it will be treated as terminating instead.
As you can see, not all errors in PowerShell all the same. Learn how to use each one to your advantage and how best to handle each one depending on the circumstances with try/catch blocks and with the use of the $ErrorActionPreference variable and the ErrorAction parameter.
About the Author
Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.