Purclot
Purclot

Reputation: 559

PowerShell error variable loses the content

I have a quite big project with many functions in there. Just 2 questions:

  1. What would be here the "best practice" regarding Error-Handling? To use a local handling per Function, or use only one Error-Logging in the Main-Section?
  2. The tricky part (!), let's have a look at the strange Error behaviour in the function F_XXX: only $_ delivers an error message, $Error[0] is here empty! Strange enough, when I start the function F_XXX separately (cut out from the module), it behaves as expected, it means: $Error[0] gives an error back. The code:

Blockquote

$ErrorActionPreference = "Stop"
Function F1
{
    try
    {
    # do something
    }
    catch
    {
        # 1. cascade Error to Main?
        # throw $Error[0].InnerException
        # or
        # local Error-Logging?
        write-MyErrorLogging -message $Error[0].InnerException
    }
}



Function F2
{
    try
    {
        # do something
    }
    catch
    {
        # 1. cascade Error to Main?
        # throw $Error[0].InnerException
        # or
        # local Error-Logging?
        write-MyErrorLogging -message $Error[0].InnerException
    }
}
Function F_XXXXXX
{
    try
    {
        cls
        write-host "The install data is copied.."
        $share = "\\my_wrong_path\sql_sources\" 
        Copy-Item $share -Destination $installDrive -Force -Recurse
    }
    catch
    {
        $Error[0] #here is nothing!
        $null -eq $Error[0] # here true
        $_.Exception # only here the error-message: wrong path!
    }
}

Blockquote

# here Main
try
{
    F1
    F2
    F_XXXXXX
}
catch
{
    write-MyErrorLogging -message $Error[0].InnerException
}

Blockquote

Upvotes: 1

Views: 808

Answers (1)

mklement0
mklement0

Reputation: 439317

  • Inside a catch block, it's best to avoid $Error[0], given that the error at hand is reliably reflected in the automatic $_ variable.

    • If you do need access to previous errors via the automatic $Error variable, use $global:Error inside modules - see the bottom section for details.
  • Unless you need to perform additional actions when an error occurs, you can let a script-terminating (fatal) error (which your $ErrorActionPreference = "Stop" statement turns all errors in your code into) bubble up the call stack until it is either caught by a try / catch statement or, in the absence of one, terminates the entire call stack (i.e., the scripts and its callers).

    • If you do need to perform additional actions, use try / catch, and place the actions inside the catch block (as well as potential cleanup actions in a finally block), followed by re-throwing the error simply by calling throw without an argument.

Thus, you can make do with a single try / catch statement in the top-level scope of your script:

# Turn all errors in this and descendant scopes into
# script-terminating (fatal) ones.
$ErrorActionPreference = 'Stop'

# ... other function definitions, without try / catch

# Top-level code that calls the other functions and catches
# any errors.
try
{
    F1
    F2
    F_XXXXXX
}
catch
{
    write-MyErrorLogging -message $_.InnerException
}

The automatic $Error variable in modules:

Strangely, up to at least PowerShell 7.2.3 (current as of this writing):

  • Errors occurring in modules - just like ones occurring outside modules - are recorded in the $Error variable that exists in the global scope.

  • However, a seemingly unused, module-local copy of $Error exists, which shadows the global variable, both in Windows PowerShell and in PowerShell (Core) up to at least v7.3.7 (current as of this writing). See this answer for the history behind this problem, and GitHub issue #20458 for a discussion about fixing it.

The workaround is to use use $global:Error from inside modules.

The behavior suggests a bug, given that the module-local copy is seemingly never touched and serves no apparent purpose.

Upvotes: 3

Related Questions