What's new with PowerShell error handling? (original) (raw)
Hitting errors -- and resolving them -- is an inevitable part of working with technology, and PowerShell is no exception.
No one writes perfect code. Your scripts might have a bug or will need to account for when a resource gets disconnected, a service hits a problem, or an input file is badly formatted. Learning how to interpret an error message, discover the root cause and handle the error gracefully is an important part of working with PowerShell. The development team behind the open source version of PowerShell 7 has improved PowerShell error handling both when you run a script and when you enter commands in a shell.
This article walks you through PowerShell error handling in a simple script and introduces several new features in PowerShell 7 that make the process more user-friendly.
How to find PowerShell 7
To start, be sure you have PowerShell 7 installed. This is the latest major release for the tool that had been called PowerShell Core up until the release of version 7. Microsoft still supports the Windows PowerShell 5.1 version but does not plan to give it the new features that the project team develops for open source PowerShell.
PowerShell 7 is available for Windows, Mac and Linux. The latest version can be installed from the PowerShell GitHub page.
On Windows, you can also use PowerShell 7 in the new Windows Terminal application, which offers improvements over the old Windows console host.
Error messages in previous PowerShell versions
A common problem for newcomers to Windows PowerShell 5.1 and the earlier PowerShell Core releases is that when something goes wrong, it's not clear why.
For example, imagine you want to export a list of local users to a CSV file, but your script contains a typo:
Get-LocalUser |= Export-Csv local_users.csv
This is what you would see when you run the script:
Before the PowerShell 7 release, this is the type of error message that would display if there was a typo in a command.
The error code contains critical information -- there's an equals symbol that doesn't belong -- but it can be difficult to find in the wall of red text.
A longtime variable gets new purpose
Did you know that PowerShell has a preference variable called $ErrorView? Perhaps not because until now, it hasn't been very useful.
The $ErrorView variable determines what information gets sent to the console and how it is formatted when an error occurs. The message can vary if you're running a script file as opposed to entering a command in the shell.
In previous versions of PowerShell, $ErrorView defaulted to NormalView -- this is the source of the wall of red text seen in the previous screenshot.
That all changes with PowerShell 7. There's a new option for $ErrorView that is now the default called ConciseView.
Errors get clearer formatting in PowerShell 7
When we run the same command with the error in PowerShell 7 with the new default ConciseView, the error message is easier to understand.
The new ConciseView option reduces the clutter and highlights the error location with a different color.
The new PowerShell error handling highlights the problem area in the command with a different color and does not overload you with too much information.
Let's fix the typo and continue testing.
Shorter errors in the shell
Another error you might encounter when writing to a CSV is that the target file is locked. For example, it's possible the file is open in Excel.
If you're using PowerShell as a shell, the new default ErrorView will now give you just the error message with no extraneous information. You can see the length of the error from Windows PowerShell 5.1 and its NormalView below.
The default error message in Windows PowerShell 5.1 provides a lot of information but not in a useful manner.
In contrast, PowerShell error handling in the newest version of the automation tool provides a more succinct message when a problem occurs due to the ConciseView option.
The ConciseView option provides a more straightforward error message when a problem with a command occurs.
You can much more easily see that the file is locked and start thinking about fixing the problem.
Learning how to explore error records
We've seen how PowerShell 7 improves error messages by providing just the information you need in a more structured manner. But what should you do if you need to dig deeper? Let's find out by continuing to use this error as an example: "The process cannot access the file … because it is being used by another process."
Taking the terror out of $Error
Every time PowerShell encounters an error, it's written to the $Error automatic variable. $Error is an array and the most recent error is $Error[0].
To learn more about the your most recent error in previous versions of PowerShell, you would explore $Error[0] with cmdlets such as Select-Object and Format-List. This type of examination is laborious: You can only expand one property at a time, and it's easy to miss vital nested information contained in a handful of properties.
For example, look at the output from the command below.
$Error[0] | Select-Object *
The $Error automatic variable in PowerShell before version 7 stored errors but was not flexible enough to give a deeper look at the properties involved.
There's no way of knowing that a wealth of valuable data lives under the properties Exception and InvocationInfo. The next section shows how to get at this information.
Learning to explore with Get-Error
PowerShell 7 comes with a new cmdlet called Get-Error that gives you a way to survey all the information held within a PowerShell error record.
Run without any arguments, Get-Error simply shows the most recent error, as you can see in the screenshot below.
The new Get-Error cmdlet in PowerShell 7 gives you an easier way to get more information about errors.
You are immediately shown the hierarchy of useful objects and properties nested inside the error record. For example, you can see the Exception property isn't a dump of information; it contains child properties, some of which have their own children.
If you want to reuse the error message in your code to write it to a log file or the Event Viewer, then you can use the following command to store the message:
$Error[0].Exception.Message
Use ErrorVariable to store error records
The Get-Error cmdlet also accepts error records from the pipeline. This is particularly handy if you use the -ErrorVariable common parameter to store errors for later inspection, which you can do with the following code:
+myErrors means "add error to $myErrors variable"
Get-LocalUser | Export-Csv local_users.csv -ErrorVariable +myErrors
Inspect the errors with Get-Error
$myErrors | Get-Error
By using Get-Error, you can see that an ErrorVariable holds information somewhat differently than the $Error variable. The error message is present in several places, most simply in a property named Message, as shown in the following screenshot.
Using the ErrorVariable parameter gives a more flexible way to log errors rather than using the $Error variable, which saves every error in a session.
Bringing it all together
You've now used Get-Error to inspect error records, both from your shell history and from an ErrorVariable, and you've seen how to access a property of the error.
The final step is to tie everything together by reusing the property in your script. This example stores errors in $myErrors and writes any error messages out to a file:
Get-LocalUser | Export-Csv local_users.csv -ErrorVariable +myErrors
if ($myErrors) {
$myErrors.Message | Out-File errors.log -Append
}
If you want to get serious about scripting and automation, then it's worth investigating the PowerShell error handling now that it got a significant boost in version 7. It's particularly helpful to store errors to a variable for later investigation or to share with a colleague.