Here is how you can verify whether an external command in PowerShell was executed successfully or not by its errorlevel. Simply by verifying the PowerShell exit code.
The terms exit code, return value or ErrorLevel can be used interchangeably.
Powershell $? operator
The PowerShell operator $?
contains True if the last operation succeeded and False otherwise.
Code language: PHP (php)
# source: # http://blogs.msdn.com/b/powershell/archive/2006/09/15/errorlevel-equivalent.aspx if( $? ) { # True, last operation succeeded } if( !$? ) { # Not True, last operation failed }
To illustrate PowerShell’s $? usage, have a look at the following DISM Cleanup-Image
command. In my Windows Server disk cleanup using DISM blogpost, I’ve shown you how to clean up your Windows Server WinSxs folder with DISM.
Those commands are easily wrapped into a PowerShell script, and here it is:
Code language: PHP (php)
$os_version = [System.Environment]::OSVersion.Version # The above returns 6.3.9600.0 for Server 2012 R2 or # 10.0.14393.0 for Server 2016. Server 2012 matches # 6.2.9200.0, and so on. # # See https://msdn.microsoft.com/nl-nl/library/windows/desktop/ms724832(v=vs.85).aspx # for more information about Windows Server versions. $cleanup = $false # Always be careful comparing strings and integers! if( $os_version -ge ( New-Object System.Version "10.0" )) { # $os_version is greater than, or equal to "10.0", so # this is Windows Server 2016. } if( $os_version -ge ( New-Object System.Version "6.3" ) -And $os_version -le ( New-Object System.Version "10.0" )) { # $os_version is greater than 6.3 and smaller than 10.0, # therefore this must be Windows Server 2012 R2 &dism.exe /online /Cleanup-Image /StartComponentCleanup /ResetBase | Write-Output if( $? ) { # dism cleanup-image was successful, set variable to True $cleanup = $true } } if ( $cleanup ) { # Dism.exe was executed Write-Host "[*] System going down for reboot in 3 seconds!" &shutdown /r /f /t 3 } else { # an error occurred Write-Host "[*] Something went wrong with DISM and Cleanup-Image, ` please perform the actions by hand." }
How can I stop PowerShell errors from being displayed in a script?
Suppress error messages in PowerShell like a pro If you don’t want to display PowerShell errors completely, you can wrap your PowerShell commands in a
Try{}
/ Catch{}
block. For example:
Code language: PHP (php)
Get-Website | % { $sitename = $_.name; try{ $handlers = Get-WebConfiguration /system.webServer/handlers/add -Location $sitename If( $handlers.scriptProcessor -like "x:php73php-cgi.exe*" ) { write-output "$sitename uses PHP 7.3" &appcmd.exe recycle apppool $sitename # Restart-WebAppPool $sitename } } catch {} }
This checks the registred handler to see if scriptProcessor contains x:php73php-cgi.exe*
. If so, recycle that website’s application pool (assuming the website and apppool names are the same).
By using a Try{}
/ Catch{}
block, you don’t see the PowerShell errors like:
Code language: PHP (php)
Get-WebConfiguration : Filename: ?z:siteswwwexample.comwwwweb.config Line number: 14 Error: There is a duplicate 'system.web.extensions/scripting/scriptResourceHandler' section defined At line:3 char:13 $handlers = Get-WebConfiguration /system.webServer/handlers/add -Loca … ~~~~~~~~~~~~~ CategoryInfo : NotSpecified: (:) [Get-WebConfiguration], COMException FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.IIs.PowerShell.Provider.GetConfigu rationCommand
PowerShell’s $LASTEXITCODE
The PowerShell $LASTEXITCODE
should be 0, since $LASTEXITCODE contains the exit code of the last Win32 executable execution.
$LASTEXITCODE the equivalent to cmd.exe %ERRORLEVEL%
, and you can use it as follows in your PowerShell scripts:
Code language: PHP (php)
&dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase if( $LASTEXITCODE -eq 0 ) { Write-Output "Command executed successfully" # do something, like `Restart-Computer -Force` } else { Write-Output "Last command failed" }
Display
Error Codes with PowerShell
September 16, 2003
Powershell
Display Error Codes with Microsoft PowerShell
My mission on this page is to show you how to display error codes, furthermore, I will explain how to translate meaningless numbers into meaningful phrases.
Topics for Error Codes in PowerShell
- Example 1: How to Create a PowerShell Function
- Example 2: Using a PowerShell Function to Display Error Codes
♣
Mission to Display Error Messages When Creating Shares
We need a vehicle to examine error codes. That vehicle will be creating a file share. For example, if the share already exists, when you run the script again, it returns error code 22. Not only can we trap this return value, but also we can ‘Switch’ 22 to a more meaningful message, such as ‘Duplicate Share’
Example 1: How to Create a PowerShell Function
Naturally, any function needs a name, in this case I chose ‘errMsg’. As this is a simple function, it only has one argument, $intErr. The rest of the function is taken up with a ‘Switch’ statement, or a series of statements corresponding to each error value.
# Microsoft PowerShell Function only
# Author: Guy Thomas
# Version 1.1 March 2008 tested on PowerShell v 1.0
Function errMsg($intErr)
{
Switch($intErr)
{
0 { «Success – Share created» }
2 { «Access denied – Permission?» }
8 { «Unknown failure» }
9 { «Invalid name» }
10 { «Invalid level» }
21 { «Invalid parameter» }
22 { «Duplicate share – Already created» }
23 { «Redirected path» }
24 { «Unknown device or directory» }
25 { «Net name not found» }
DEFAULT { «$intErr has an Unknown value» }
}
}
Note 1: This code does not do anything by itself, it is merely a stage in producing the script.
Example 2: Using a PowerShell Function to Display an Error Code
Preparation and Pre-requisites
To understand Example 2, I suggest that you familiarise yourself with the logic of my ‘Create a Share‘ example. Then you can match my thinking to the second portion of the script below.
When you understand my script, copy and paste the code into PowerShell, or better, create a cmdlet with .ps1 extension. Then call that file from within PowerShell with ./filename.
# Using a PowerShell Function
# Author: Guy Thomas
# Version 2.3 March 2008 tested on PowerShell v 1.0
Function errMsg($intErr)
{
Switch($intErr)
{
0 { «Success – Share created» }
2 { «Access denied – Permission?» }
8 { «Unknown failure» }
9 { «Invalid name» }
10 { «Invalid level» }
21 { «Invalid parameter» }
22 { «Duplicate share – Already created» }
23 { «Redirected path» }
24 { «Unknown device or directory» }
25 { «Net name not found» }
DEFAULT { «$intErr has an Unknown value» }
}
}
$FolderPath = «C:Temp»
$ShareName = «Temporary»
$Type = 0
$Description = «Guy and PowerShell»
$class = «Win32_share»
$objWMI = [wmiClass] ‘Win32_share’
$Success=$objWMI.create($FolderPath, $ShareName, $Type)
errMsg($Success.returnValue)
Note 1: Working backwards! On the last line, errMsg($Success.returnValue) runs a number through the function, which I created in the first part of the script. The result is a meaningful message controlled by that function’s Switch statement.
Note 2: Next, let us study the $Success variable. $Success=$objWMI…. results in an attempt to create the share, however, it exits, therefore the .returnValue is 22.
See more WMI share tasks for PowerShell »
»
Summary: Error Codes in PowerShell
All good Microsoft PowerShell scripts should have error correcting code. The first step is to trap the .returnValue; closely followed by switching the number for a meaningful phrase.
If you like this page then please share it with your friends
See more PowerShell share examples including WMI
• PowerShell WMI • Create Win32_Share • WMI Shares • Free Permissions Analyzer Tool
• Get-Acl • PowerShell Share Error Codes • Win32_ComputerSystem • PowerShell 3.0 CIM
• Windows PowerShell • Free WMI Monitor • Cacls • Query • PowerShell Printer Scripts
Please email me if you have a example scripts. Also please report any factual mistakes, grammatical errors or broken links, I will be happy to correct the fault.
About The Author
Guy Thomas
Welcome to my Getting Started with Windows PowerShell series!
Why Handle Errors?
Error handling may take some extra time to create, however, it can really save you time in the long run. Let’s say you have a script that performs the following tasks:
-
Takes information from a database / file and stores it in $userAccounts
-
Uses a loop to perform processing on each $user in $userAccounts
-
Update a user’s AD information if the user is in the list
-
Disable any accounts that are not in the list
-
The script runs, and seems to be working great for a few weeks. Then, all of a sudden, the database server goes down, and $userAccounts is empty. The script runs, doesn’t see any users, and what likely happens next is all user accounts are deleted. While this example is a little extreme, it is something that can happen. There are also a lot of in-between cases that can make error handling worth it. It’s a good mindset to have when you’re writing scripts as you can write to incorporate it off the bat.
Errors in PowerShell
Errors in PowerShell are stored in the automatic variable $error. You can see how many errors you’ve encountered in your session by using:
$error.count
This is essentially an array of errors, to access the first value you can use:
$error[0]
Let’s take a look at $error[0] more closely, via:
$error[0] | Get-Member
We can view information on the command that raised the error via:
$error[0].InvocationInfo
It looks like the line property contains the full command we used when the error was encountered.
$error[0].InvocationInfo.Line
The exception that raised the error can be accessed via:
$error[0].Exception
We can also get more information about the exception via:
$error[0].Exception | Get-Member
Here you can see the TypeName: [System.UnauthorizedAccessException], and the various methods and properties of the exception. The exception TypeName is used later when we catch specific errors.
Looks like to get the exception’s message in string format we’d use:
$error[0].Exception.Message
Now that we’ve dug into the automatic variable $error, let’s move on to the different types of errors we can encounter.
Terminating Errors
Terminating errors in PowerShell mean that the script can no longer continue to run with the information it has encountered, or has been given. If you do not have a way to catch the error, it will likely display the nasty red error text you’ve likely seen before. Terminating errors halt anything running in the pipeline. This terminates output, aside from the error message of the terminating error.
Here’s an example of a terminating error that was caused by calling a command that does not exist:
Get-TerminatingError
Non-Terminating Errors
Non-terminating errors in PowerShell do not stop the pipeline from executing. These errors are handled internally by the Cmdlet, and are not able to be captured via error handling. There are ways, however, to make PowerShell treat non-terminating errors like terminating errors. That way we can use error handling to capture what’s going on.
Here’s an example of a non-terminating error (access denied to a subfolder), when attempting to list all folders and subfolders in «C:Windowsappcompat».
Get-ChildItem -Path 'C:Windowsappcompat' -Recurse
Force Non-Terminating Errors to Terminate
You can force non-terminating errors to become terminating errors in PowerShell. There are a couple ways you can do this. Why would you want to do this, you ask? Since non-terminating errors cannot be handled by us, we need to force them to terminate, and gain the functionality of handling the error ourselves. This is not always needed, but it is good to know that you can do it if you come across a use case for yourself.
$errorActionPreference
You can do it at a global way for the session via the $ErrorActionPreference variable. This is a special variable in PowerShell that allows you to control what happens when a non-terminating error is encountered.
Here are the values for $ErrorActionPreference.
-
Stop
-
Display error, and stop execution.
-
-
Inquire
-
Display error, and ask to continue.
-
-
Continue (Default)
-
This is the default setting. Display error, then continue execution.
-
-
Suspend
-
This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.
-
-
SilentlyContinue
-
No error is displayed, execution is continued.
-
Let’s see this in action.
Get-ChildItem -Path 'C:Windowsappcompat' -Recurse;Write-Host 'Test'
As you can see, with a non-terminating error, the next command in the sequence is executed. Therefore we see ‘Test’. Let’s set the $errorActionPreference automatic variable to Stop, and re-run the same command.
$ErrorActionPreference = 'Stop'
Get-ChildItem -Path 'C:Windowsappcompat' -Recurse;Write-Host 'Test'
Use the Command’s -ErrorAction Parameter
Cmdlet’s and functions/scripts/modules that use [cmdletbinding()] enable utilization of the -ErrorAction common parameter. This parameter allows you to specify different actions to take when an error is encountered.
-
Stop
-
Display error, and stop execution.
-
-
Inquire
-
Display error, and ask to continue.
-
-
Continue (Default)
-
This is the default setting. Display error, then continue execution.
-
-
Suspend
-
This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.
-
-
SilentlyContinue
-
No error is displayed, execution is continued.
-
-
Ignore
-
The same as SilentlyContinue, but as SilentlyContinue still adds the message to the $error automatic variable, Ignore does not do this.
-
Let’s set our $errorActionPreference to Continue, and then look at using the Cmdlet Get-ChildItem‘s -ErrorAction parameter.
We’re setting $errorActionPreference back to Continue as earlier we set it to Stop for our other example. Continue is the default value.
$ErrorActionPreference = 'Continue'
Get-ChildItem -Path 'C:Windowsappcompat' -Recurse -ErrorAction Stop;Write-Host 'Test'
Error Handling
There are a few different ways to to handle errors in PowerShell. The best way of course, is to never let them happen in the first place! To do that, it is good to have some data validation in place via an if statement, or whichever method suites your needs.
If a terminating error occurs, we can use Try/Catch/Finally blocks to perform different actions. If it is non-terminating, we can force it to become a terminating error, and then choose how to continue.
Validation
The simplest method of validation is the if statement.
If you’d like to run these examples yourself, go ahead and fire up the PowerShell ISE. Save a file in C:PowerShell as part11.ps1.
An if statement is constructed as follows:
if (condition -eq $true) {
Do-Stuff
} else {
Do-Other-Stuff
}
The condition can be anything that resolves to a true value. That includes running a command that output (other than and error) occurs! Check this out:
if (Get-ChildItem Z: -ErrorAction SilentlyContinue) { Write-Host 'I can list the contents of Z:!' } else { Write-Host 'I cannot list the contents of Z:!' }
This does not return true, as expected, and thus we see the message from the code in the else portion of the if statement. We use the -ErrorAction common parameter with the value SilentlyContinue to suppress the error from being displayed to the end user of the script. No need in this case, as we’re simply using the command for validation.
You can also use variables with the if statement to see if they are blank.
$myVariable = $null if ($myVariable) { Write-Host "We have information! Let's do stuff." } else { Write-Host "`$myVariable is empty :(" }
The variable is empty, so the code in the else portion of the if statement is executed.
Try/Catch/Finally
The Try, Catch, and Finally blocks in PowerShell allow us to capture terminating errors.
The Try block contains the code you’d like to execute, and catch any potential errors that happen.
The Catch block contains the code you’d like to execute after a terminating error has occurred. The current error will be accessible via the automatic variable $_.
The Finally block contains the code you’d like to run after the event has occurred. This is good for cleanup tasks.
It is worth noting that finally block is not required.
Here is an example using Try/Catch/Finally:
Try { $command = 'Invoke-FakeCommand' Write-Host "Attempting to run: [Invoke-Expression -Command $command]"`n Invoke-Expression -Command $command } Catch { Write-Host $_.Exception.Message`n } Finally { Write-Host "Clean up: `$commmand = `$null"`n $commmand = $null }
The code in the Try block executes and we see the output of Write-Host. We then see the error message that occurs, as our Catch block is writing out $_.Exception.Message. As we learned earlier, that is the string value of the exception that raised the error.
We then see the output from the Write-Host command in our Finally block. We use the finally block to free up the $command variable.
This version of Try/Catch will catch any terminating errors that are raised. To capture specific exceptions, you’ll want to use the exception’s TypeName.
Catch Specific Errors
Let’s take a look at the following:
Try { Get-ThisWontWork } Catch [System.Management.Automation.CommandNotFoundException] { Write-Host "Command not found!"`n -ForegroundColor Red Write-Host "Message: [$($_.Exception.Message)"] -ForegroundColor Red -BackgroundColor DarkBlue }
The exception was caught, and the code in the Catch block was executed.
Now let’s try this…
Try { Get-ChildItem -Path Z: -ErrorAction Stop Get-ThisWontWork } Catch [System.Management.Automation.CommandNotFoundException] { Write-Host "Command not found!"`n -ForegroundColor Red Write-Host "Message: [$($_.Exception.Message)"] -ForegroundColor Red -BackgroundColor DarkBlue }
Ahh! An error that wasn’t handled! Luckily we can add multiple catch blocks.
Let’s add another Catch block.
Catch {
Write-Host $_.Exception.Message
}
Try { Get-ChildItem -Path Z: -ErrorAction Stop Get-ThisWontWork } Catch [System.Management.Automation.CommandNotFoundException] { Write-Host "Command not found!"`n -ForegroundColor Red Write-Host "Message: [$($_.Exception.Message)"] -ForegroundColor Red -BackgroundColor DarkBlue } Catch { Write-Host $_.Exception.Message }
Now the exception from Get-ChildItem command is caught since the catch-all Catch block code is executed.
Getting Error Information
It can be handy to have a shortcut that shows you error information, which you can use to create specific Catch blocks. To do this, I created a function that I call in the Catch block which utilizes some validation itself! Here is the function:
function Get-ErrorInformation { [cmdletbinding()] param($incomingError) if ($incomingError -and (($incomingError| Get-Member | Select-Object -ExpandProperty TypeName -Unique) -eq 'System.Management.Automation.ErrorRecord')) { Write-Host `n"Error information:"`n Write-Host `t"Exception type for catch: [$($IncomingError.Exception | Get-Member | Select-Object -ExpandProperty TypeName -Unique)]"`n if ($incomingError.InvocationInfo.Line) { Write-Host `t"Command : [$($incomingError.InvocationInfo.Line.Trim())]" } else { Write-Host `t"Unable to get command information! Multiple catch blocks can do this :("`n } Write-Host `t"Exception : [$($incomingError.Exception.Message)]"`n Write-Host `t"Target Object : [$($incomingError.TargetObject)]"`n } Else { Write-Host "Please include a valid error record when using this function!" -ForegroundColor Red -BackgroundColor DarkBlue } }
Here is the full code I’ll be running for this example:
function Get-ErrorInformation { [cmdletbinding()] param($incomingError) if ($incomingError -and (($incomingError| Get-Member | Select-Object -ExpandProperty TypeName -Unique) -eq 'System.Management.Automation.ErrorRecord')) { Write-Host `n"Error information:"`n Write-Host `t"Exception type for catch: [$($IncomingError.Exception | Get-Member | Select-Object -ExpandProperty TypeName -Unique)]"`n if ($incomingError.InvocationInfo.Line) { Write-Host `t"Command : [$($incomingError.InvocationInfo.Line.Trim())]" } else { Write-Host `t"Unable to get command information! Multiple catch blocks can do this :("`n } Write-Host `t"Exception : [$($incomingError.Exception.Message)]"`n Write-Host `t"Target Object : [$($incomingError.TargetObject)]"`n } Else { Write-Host "Please include a valid error record when using this function!" -ForegroundColor Red -BackgroundColor DarkBlue } } Try { Get-ChildItem -Path Z: -ErrorAction Stop Get-ThisWontWork } Catch [System.Management.Automation.CommandNotFoundException] { Write-Host 'Command not found Catch block executed!' } Catch { Get-ErrorInformation -incomingError $_ }
So now, if we wanted to catch this exception on it’s own, we would need to add a catch block for [System.Management.Automation.DriveNotFoundException].
Catch [System.Management.Automation.CommandNotFoundException] {
Write-Host 'Command not found Catch block executed!'
}
Let’s add that to our code and run the following:
function Get-ErrorInformation { [cmdletbinding()] param($incomingError) if ($incomingError -and (($incomingError| Get-Member | Select-Object -ExpandProperty TypeName -Unique) -eq 'System.Management.Automation.ErrorRecord')) { Write-Host `n"Error information:"`n Write-Host `t"Exception type for catch: [$($IncomingError.Exception | Get-Member | Select-Object -ExpandProperty TypeName -Unique)]"`n if ($incomingError.InvocationInfo.Line) { Write-Host `t"Command : [$($incomingError.InvocationInfo.Line.Trim())]" } else { Write-Host `t"Unable to get command information! Multiple catch blocks can do this :("`n } Write-Host `t"Exception : [$($incomingError.Exception.Message)]"`n Write-Host `t"Target Object : [$($incomingError.TargetObject)]"`n } Else { Write-Host "Please include a valid error record when using this function!" -ForegroundColor Red -BackgroundColor DarkBlue } } Try { Get-ChildItem -Path Z: -ErrorAction Stop Get-ThisWontWork } Catch [System.Management.Automation.CommandNotFoundException] { Write-Host 'Command not found Catch block executed!' } Catch [System.Management.Automation.DriveNotFoundException] { Write-Host 'Get-ChildItem drive not found Catch block executed!' } Catch { Get-ErrorInformation -incomingError $_ }
There we go! Now all our errors are handled, minus the ones we don’t know about yet.
Homework
-
Figure out why when multiple Catch blocks are used, it doesn’t pass along the execution information (unable to get command/line).
-
Let me know why this is!
-
-
Where else could I have added error handling in any of these examples?
I hope you’ve enjoyed the series so far! As always, leave a comment if you have any feedback or questions!
-Ginger Ninja
[Back to Top]
Hey there!
We often come across Error codes (or Exit Codes) in windows operating system environment, but most of the time we’re unsure what these numerical values actually mean, unless we try Googling it out the number.
WHAT IS ERROR EXIT CODE ?
The Exit code or Return code or System Error code of a process in computer programming is a small number passed from a child process (or CALLEE) to a parent process (or CALLER) when it has finished executing a specific procedure or delegated task.
Windows uses 32-bit signed integers as exit codes.If a process fails initialization, a Windows system error code may be returned
So, in order to decipher these numbers, I wrote two PowerShell functions –
- Import-WindowsErrorCode : To import all these error Code information from series of MSDN URL’s mention HERE
- Get-WindowsErrorCodeInfo : To query the information of a specific Error Code from imported data..
PURPOSE :
- Quickly lookup for Error description depending upon the Windows Error Code/ Exit Code passed.
- Adding some verbosity to plain simple Error/Exit Codes.
- Could be utilized for a better Error Handling and better Investigation and Analysis.
Like in the below screenshot I passed the exit code returned while installing a .MSI file to the function Get-WindowsErrorCodeInfo to get information like ErrorString, Description and corresponding Hexa-Decimal value
Similarly, more information can be queried from the Error codes generated by running .exe or .MSU files, like in below image.
IMPORTING/SAVING SYSTEM ERROR INFORMATION :
Run the below mentioned function from a Powershell console and it will make web Queries to Microsoft MSDN Web sites to capture all error codes, like in the following screenshot.
Its nothing but a series of Invoke-WebRequest’s on MSDN URL’s to web scrap the error code information, Sort them and make them in presentable format, which could be easily parsed.
Now you can either convert and Save the results locally in a JSON file (You can also choose CSV) ,
Or, convert it to JSON , then COPY the result
and upload it to your Github Gist.
HOW TO RUN IT :
Get the script from HERE on my Github Repository and you can run the Import-WindowsErrorCode function to save the information and then Get-WindowsErrorCode to query the error codes.
Following are some ways to Query information using the Error/Exit Codes
- You can query single exit code or can pass multiple value through Pipeline.
By default it queries my Github Gist available over internet.
- You can also query the exit code information from a local JSON file.
- Or you can dump the JSON data on your Github Gist and retrieve the exit code information like below, by passing your URL under -GitHubJSONUrl switch
- In case you don’t mention any specific exit code or path; The function will return all exit code available from my Github gist, which I would advise to be the preferable method. Which will fetch around 2700 Exit Codes
NOTE :
You can skip the importing part and directly use the Get-WindowsErrorCodeInfo function which will query my Github gist URL by default ( but Requires Internet Connectivity).
I’ve taken care of the importing part for you guys 🙂 by importing and saving the all Windows System Error codes on my public Github gist.
Updated on 21 July :
From few comments on Reddit link sharing, I’ve learned that this may not be the smartest way to get the error code description. You can also use windows Native command (Net Helpmsg Errorcode) to get the description which uses native windows API to get this information.
but after some hit and trial I’ve found that this approach has some limitations and doesn’t identifies every exit code we fetched form Microsoft websites. Like in the example in following screenshot for ERROR_INTERNET_* type error messages
I think both approaches are good and there is a trade-off between Speed and More information. Choice is yours!
Hoping you find it useful in your day to day work, comment and let me know for any improvements! Happy Learning 🙂
@SinghPrateik
As with any programming language, code will have errors and troubleshooting those problems can be difficult. Thankfully, PowerShell has a rich error object and several powerful tools to help debug your code.
With PowerShell 7, these tools become even more useful and error handling even easier. As the language evolves and becomes used in more places than ever, being able to quickly and efficiently troubleshoot a problem will prove invaluable to integrating the language into common workflows.
Table of Contents
- Understanding Errors in PowerShell
- Terminating Errors
- Non-Terminating Errors
- Error Views
- The Error Object Behind the Scenes
- The New Get-Error Cmdlet
- Exception
- TargetObject
- CategoryInfo
- FullyQualifiedErrorId
- InvocationInfo
- ScriptStackTrace
- Conclusion
Understanding Errors in PowerShell
Broadly speaking, PowerShell errors fall into two categories, terminating and non-terminating. Though these concepts are worth articles in their own right, a terminating error implies that code execution is stopped when the error is thrown. A non-terminating error implies that the code will continue despite an error message being shown.
Terminating Errors
As you can see below, the text “This should never be shown”, is not shown, as the terminating error stops code execution. The function throw
will always return a terminating error.
Figure 1 – Terminating Error Output
Non-Terminating Errors
It is more difficult to arbitrarily generate a non-terminating error, but one easy way is to use the Get-ChildItem
cmdlet and ask the cmdlet to find a nonexistent directory. As you can tell the command Write-Host "This text will show!"
, does in fact appear.
Figure 2 – Non-Terminating Error Output
You can turn most non-terminating errors into terminating errors by modifying an individual cmdlet’s ErrorAction
to Stop
. For example, Get-ChildItem "missing_dir" -ErrorAction 'Stop'
Error Views
You might notice that in the previous output, there are two different views of the error information. Figure 1 shows the NormalView
of the $ErrorView
preference variable. This view was standard and traditional until PowerShell 7. Starting with PowerShell 7, the default view has changed to what you see in Figure 2 and that is of the ConciseView
. It dispenses with much of the decoration around the output, but as you might be able to tell, some information is not made available.
The Error Object Behind the Scenes
Underlying the data behind the error output is the $Error
object that is populated by PowerShell when errors are thrown. To view this data, you are able to output and walk through the information. The traditional way to get the last error thrown is by calling $Error[0]
. This uses array notation to reference the error.
Figure 4 – $Error Object
If you happen to mistype this command, you will overwrite the first object in the error collection with the new error, so be careful when referencing this object.
As you can see there is the same error as originally shown, but we want to view more of the data. By selecting all of the properties, we are able to see what’s available. As we will talk about in the next section, the Get-Error
cmdlet provides a rich view of this data, but it’s important to understand what is going on underneath.
Figure 5 – Error Object Properties
By walking through each property we can see what information exists between the Get-Error
cmdlet and the $Error
object itself.
Figure 6 – Error Object Exception Properties
The New Get-Error
Cmdlet
That brings us to the next PowerShell 7 addition and that is the Get-Error
cmdlet. To expand upon the ConciseView
and show far more detail, we can run the Get-Error
cmdlet and see the expanded details of the last error thrown.
Related article: Error Handling With PowerShell Try Catch Blocks
Figure 3 – Get-Error Output
There is a lot of information shown here, so let’s break down what is useful.
Exception
- Type – Basic Exception Information
- ErrorRecordMost of this information is from the
$Error
object itself. TheTargetObject
,CategoryInfo
, andFullyQualifiedErrorId
are all duplicated further in theGet-Error
output. What is useful is theException
data.- Type – An exception, but could be referencing the parent exception
- Message – The human-readable error message
- HResult – Traditional numerical error code that Windows has used since the early days of the operating system
- ItemName – The same as the
TargetObject
shown later in theGet-Error
output - SessionStateCategory – A series of values that errors fall into, this is an
enum
underneath - TargetSite – A set of information that exposes some of the internal PowerShell engine values and where the error itself is coming from
- StackTrace – This is the actual method signature of where the error itself came from and can help aid in why an error was shown
- Message – The human-readable error message
- Source – This is the source of where the error is coming from
- HResult – As discussed above, the traditional numerical error code from Windows
TargetObject
The object that the function, cmdlet, or code targets, in this case D:\missing_dir
CategoryInfo
A concatenated view of several different properties, breaking down to the below format:
<Error>: (<TargetObject>:<ObjectType>) [<Originating CmdLet>], <Exception Type>
FullyQualifiedErrorId
The FullyQualifiedErrorId
is Message
property of the exception object combined with the fully-qualified name of the class where the exception originated.
InvocationInfo
- MyCommand – The originating cmdlet or function throwing the error
- ScriptLineNumber – Location within the file or ScriptBlock that the error is thrown
- OffsetInLine – The location within the line that the error was thrown
- HistoryId – The location from within the
Get-History
cmdlet that the error was thrown - Line – The command throwing the error
- PositionMessage – Combined information for the error
- InvocationName – The cmdlet or function throwing the error
- CommandOrigin – In what context the error was thrown
ScriptStackTrace
Contained here is information on where in a script the error occurred. In this case, the error occurred on line 1
, but this will reflect the line of the error in the given ScriptBlock
.
Conclusion
Unlike other programming languages, PowerShell provides a very rich error object to figure out what went wrong and help to debug troublesome code. With PowerShell 7, the ability to decipher errors is even easier with the introduction of the Get-Error
cmdlet. Furthermore, the ConciseView
of the ErrorAction
preference will keep the command line free from clutter and make coding even easier!