9

I have PS module that contains a number of scripts for individual functions. There is also a "library" script with a number of helper functions that get called by the functions used in the module.

Let's call the outer function ReadWeb, and it uses a helper function ParseXML.

I encountered an issue this week with error handling in the inner helper ParseXML function. That function contains a try/catch, and in the catch I interrogate:

$Error[0].Exception.InnerException.Message

...in order to pass the error back to the outer scope as a variable and determine if ParseXML worked.

For a particular case, I was getting an indexing error when I called ReadWeb. The root cause turned out to be the $Error object in the Catch block in ParseXML was coming back $Null.

I changed the error handling to check for a $Error -eq $Null and if it is, use $_ in the Catch to determine what the error message is.

My question is: what would cause $Error to be $null inside the Catch?

7
  • Did you try to isolate the problem into a reduced test case which you can publish here? Commented Dec 11, 2013 at 14:39
  • @Neolisk I haven't because of the trouble involved in the various scopes. I have a module with a script file that calls another function in another module that calls a function in the library. Commented Dec 11, 2013 at 14:41
  • Well, it could help to know more about your problem - code, classes, anything. Right now it sounds weird, but that's about it. Commented Dec 11, 2013 at 14:47
  • I can try to add some more detail a little later today if necessary. It's non-trivial to create a reduced case for this. Commented Dec 11, 2013 at 15:33
  • I understand. However, it's also non-trivial to give an answer without knowing any specifics. :) Commented Dec 11, 2013 at 16:22

3 Answers 3

4

Note: The following applies to Windows PowerShell as well as to PowerShell (Core) versions up to a least v7.3.7 (current as of this writing).

  • Accessing the error at hand inside a catch block should indeed be done via the automatic $_ variable.

  • Inside a module, $Error isn't $null, but surprisingly is an always-empty collection (of type System.Collections.ArrayList); therefore, $Error[0] - which in catch blocks outside modules is the same as $_ - is unexpectedly $null in modules:

    • What technically happens is that modules have an unused, module-local copy of $Error, which shadows (hides) the true $Error variable that is located in the global scope.

      • When an error is automatically recorded from inside a module, however, it is added to the global $Error collection - just like in code running outside of modules.

      • As it turns out, the unused, useless module-level copy of $Error is an accidental remnant of functionality that was never implemented: see this answer for the history, and GitHub issue #20458 for a discussion about fixing the problem.

    • Therefore, a workaround is to use $global:Error instead (which is only necessary if you need access to previous errors; as stated, for the current one, use $_).

The following sample code illustrates the problem:

$Error.Clear()

# Define a dynamic module that exports sample function 'foo'.
$null = New-Module {

  Function foo {
    try {
      1 / 0     # Provoke an error that can be caught.
    }
    catch {
      $varNames =   '$Error', '$global:Error', '$_'
      $varValues =   $Error,   $global:Error,   $_
      foreach ($i in 0..($varNames.Count-1)) {
        [pscustomobject] @{
          Name = $varNames[$i]
          Type = $varValues[$i].GetType().FullName
          Value = $varValues[$i]
        }
      }
    }
  }

}

foo

Output; note how the value of $Error is {}, indicating an empty collection:


Name          Type                                     Value
----          ----                                     -----
$Error        System.Collections.ArrayList             {}
$global:Error System.Collections.ArrayList             {Attempted to divide by zero.}
$_            System.Management.Automation.ErrorRecord Attempted to divide by zero.

Sign up to request clarification or add additional context in comments.

Comments

0

Edit: answer based on Powershell 3.

$error is an automatic variable handled by Powershell: 3rd § of the LONG DESCRIPTION in about_Try_Catch_Finally.

It is considered as the context of the Catch block, thus being available as $_. Since Catch block is a different block than Try, the $error automatic variable is reset and valued $null.

3 Comments

Recommending use of $_ to access the error at hand instead of $Error[0] is the right thing to do. The link is dead now, but perhaps learn.microsoft.com/en-us/powershell/scripting/lang-spec/… is a suitable replacement. However, I don't think your explanation re $Error is correct (at least not in 5.1+), given that outside modules, $Error[0] is the same as $_. Inside modules, it seems that an unused module-level copy of $Error hides the global one (possibly a bug), which $global:Error can work around.
@mkelement0: question was legit 8 years ago, with powershell 2 to 4 available, so was my answer :)
The question is still legit, but your answer never was, from what I can tell (as argued in my previous comment).
0

$error and try / catch are different beasts in PowerShell.

try / catch catches terminating errors, but won't catch a Write-Error (cause it's non terminating).

$error is a list of all errors encountered (including ones swallowed up when -ErrorAction silentlycontinue is used).

$_ is the current error in a try/catch block.

I'd guess that your underlying function calls Write-Error, and you want that to go cleanly into a try/catch. To make this be a terminating error as well, use -ErrorAction Stop.

1 Comment

Unfortunately, nothing sets ErrorAction at all, and I did try to set $ErrorActionPreference to check this behavior as well. I'm not sure how $Error can be $null inside the catch block, though. If we are inside a catch block, a terminating error has occurred regardless.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.