Using the global error collection stored in the $Error variable to retrieve detailed error information in a powershell script

I was recently working with a developer who wanted to know if there is a way to get the error when they call the Invoke-Expression cmdlet. He was using the $LastExitCode to determine if it succeeded or not. Unfortunately, the $LastExitCode was not returning the error he was expecting.

A better way of checking for errors is to use the $Error variable. Since the $Error variable is an array, it will retain a list of all the errors that has occurred for the current session that you are running. Unless you explicitly clear the error, you will be able to retrieve all the errors since the start of the session.

You can clear the error buffer by typing: 

   1: $Error.Clear()
   2: 

If you want to determine how many errors have been encountered since the start of the session or the time you last cleared buffer, you can type:

   1: $Error.Count
   2: 

The errors are indexed from 0 to the n-1, where n is the number of errors. You can access the most recent error by typing:

   1: $Error[0]
   2: 

You can also get more detail by checking the InvocationInfo property. Here’s an example of how you might use this. The code below will generate an error:

   1: $Error.Clear()   
   2: $myScriptTest = "dir a: 
   3: dir b:   
   4: dir c:" 
   5: Invoke-Expression $myScriptTest
   6: 

Assuming that you do not have a Drive A: nor a Drive B: but only have a Drive C: and having cleared the error prior the the script’s execution, one would conclude that $Error.Count would return a value of 2, however, if you check the $Error.Count, it actually returns 3 errors:

 PS C:\WINDOWS\system32> $Error.Count
 3

You might be wondering what the 3rd error is, right? If you look closely to the script, on line 2, the string does not have a closing double quote to terminate the string. Because of this, it counts that as a separate error. If you want to only check for the errors when the Invoke-Expression cmdlet is called, you will need to move the $Error.Clear() call just prior to the Invoke-Expression cmdlet like so:

   1: $myScriptTest = "dir a: 
   2: dir b:    
   3: dir c:" 
   4: $Error.Clear()
   5: Invoke-Expression $myScriptTest
   6: 

If we want to inspect all the individual errors encountered within the Invoke-Expression call only, we can do so by drilling into the InvocationInfo property. For example, if we want to check the last error encountered when the Invoke-Expression cmdlet was called, which in our example above is when the dir b: command was processed, we can easily do this by using $Error[0] and specifying the InvocationInfo property like below:

 PS C:\WINDOWS\system32> $Error[0].InvocationInfo
 
 
 MyCommand        : Get-ChildItem
 BoundParameters  : {[Path, System.String[]]}
 UnboundArguments : {}
 ScriptLineNumber : 2
 OffsetInLine     : 4
 HistoryId        : 115
 ScriptName       :
 Line             : dir b:
 PositionMessage  :
                    At line:2 char:4
                    + dir <<<<  b:
 InvocationName   : dir
 PipelineLength   : 1
 PipelinePosition : 1
 ExpectingInput   : False
 CommandOrigin    : Internal

You will notice that there is a wealth of information regarding the error. For instance, you might be interested in following:

  • The Line in the script block where it is failing.
  • The Column Offset where the error is occurring.
  • The Line being processed during the failure.

Because the errors are ordered from the most recent to the oldest, I used the Sort-Object cmdlet to sort the object from oldest to most recent. Below is an example on how you may enumerate the errors from oldest to most recent:

   1: $Error | Sort | % { $ScriptError = $_.InvocationInfo; Write-Host ("Error on line " + $ScriptError.ScriptLineNumber + ", column " + $ScriptError.OffsetInLine + ' executing "' + $ScriptError.Line + '"')}
   2: 

In future posts to this blog  I will show you how you can incorporate all these techniques together with the try catch block to include error handling in your scripts.

Until next time!