I have a quite big project with many functions in there. Just 2 questions:
- 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?
- 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
CodePudding user response:
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.
- If you do need access to previous errors via the automatic
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 atry
/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 thecatch
block (as well as potential cleanup actions in afinally
block), followed by re-throwing the error simply by callingthrow
without an argument.
- If you do need to perform additional actions, use
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.
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.