10

I'm encountering a peculiar issue with Powershell. I'm catching an Exception in a catch block, but the global $Error object is not being populated.

A trivial example, where this would behave as expected is this:

function Bar
{
  Foo
}

function Foo
{
  try
  {
    $Error.Clear()
    throw "Error!"
  }
  catch
  {
    "Caught an error - current error count $($Error.Count)"  
  } 
  finally
  {
    "Cleaning up - current error count $($Error.Count)"  
  }
}

Output is as you would expect if you call Bar

Caught an error - current error count 1
Cleaning up - current error count 1

The code I'm having trouble with is nearly identical, except that it loads Foo from a module. Not sure if this is a bug, or simply something I don't understand (will have to check my Powershell in Action book!)

If I save Foo off to a module - Foo.psm1

function Foo
{
  try
  {
    $Error.Clear()
    throw "Error!"
  }
  catch
  {
    "Caught an error - current error count $($Error.Count)"  
  } 
  finally
  {
    "Cleaning up - current error count $($Error.Count)"  
  }
}

Export-ModuleMember -Function Foo

Then I perform the following

Import-Module .\Foo.psm1
$Error.Clear()
"Current error count $($Error.Count)"
Foo
"Current error count $($Error.Count)"

I end up with

Current error count 0
Caught an error - current error count 0
Cleaning up - current error count 0
Current error count 1

Notice that Foo no longer sees any changes made to $Error. So the module-ification of the code is changing error propagation behavior. Can anyone chime in with the reasoning behind this?

I should note that I can get at the specific caught exception via the automatic variable $_, but I'm looking to get a hold of the entire collection at this point in the call stack.

1
  • 1
    So my first line above is wrong.. the global $Error collection is modified, but the module scoped $Error collection is not. Commented Jul 12, 2012 at 14:22

2 Answers 2

14

As Ethan mentioned, you need to look at $global:error to see all of the errors. Modules always have they're own $error variable but it's (mostly) not used.

background: at one point, we had considered isolating errors that occurred in module code to the module scope but ultimately decided to add all errors to the global error collection since it is essentially a log of errors (like an in-memory eventlog.) Unfortunately the module scoped $error wasn't removed before the release and so you need to use the global scope qualifier to access the "real" $error variable.

Bruce Payette, Microsoft Corporation

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

2 Comments

Bruce - thanks for the historical background. This is exactly the type of info I was looking for.
Possibly fixing this problem, which is still present as of PowerShell (Core) v7.3.7, is now being discussed in GitHub issue #20458.
10

I hadn't noticed that before, but perhaps the $error collection is scoped to the module like any other variables. Try comparing the values of the following two explicitly scoped variables at key points in your test script:

"Errors - Global: {0}; Module: {1}" -f $global:error.count, $script:error.count

Let me know how you get on.

5 Comments

Could be that $error.clear() copies the global variable to the local scope and clears that.
A way to get the Error variable in the module scope would be Get-Variable -Name Error -Scope 2. @Ethan , since you have PS in Action you may want to look through section 9.4.5. How scopes work in script modules.
I feel a little silly now... I can't check at the moment, but I suspect the scoping is likely the issue here. Totally slipped my mind (which is worse, since I do have a few $script: level vars in the code)! I will recheck 9.4.5 -- thanks Andy! I guess the peculiarity then would be that accessing $Error in a module doesn't have a reasonable default scope - after all, the exception surfaced in the module, so you would think $Error would default to the array where the modules errors were collected.
The errors are indeed accessible through $global:Error. I skimmed 9.4.5 of PS in action... I understand the scope resolution rules. What I still don't understand is why there would even be a module level $Error object, when throwing within the module will not modify it. Peculiar. IMHO, violates principle of least surprise as you could $Error.Add() within the module to modify the locally scoped collection, but any sort of throw modifies the globally scoped collection.
Sometimes I think the PS prefix stands for Principle of Surprise :)

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.