Skip to content

Is there actually a best way to handle errors? #37

Open
@Jaykul

Description

@Jaykul

https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Best%20Practices/err-05-avoid-testing-for-a-null-variable-as-an-error-condition.md

First of all, let's not have a misleading discussion. The original guideline uses a bad example. Get-ADUser throws an exception when you use the -Identity parameter and it can't find anything. There's obviously no value in writing a warning if you didn't suppress the error, and it's equally silly to let an exception spew and then still test for null -- you need to either suppress the error or use it instead of testing for null.

Let's try a different example. I have a module which requires the user to configure multiple connection strings and a license before it can continue. Imagine that it was this simple to verify that you had, in fact, configured it:

$Config = Get-Content $ConfigurationFile

This will cause an error and an empty variable if the ConfigurationFile doesn't exist, but does not throw (it's not terminating) by default. There are a few ways I could handle that in PowerShell.

For what it's worth: the pythonic way is always to just charge ahead, never test, and just deal with the exceptions (or let them output, if they're clear enough on their own). The old C/C++ way is to always check the hResult, there are no exceptions. The Java and C# way usually involve handling exceptions, but not quite as aggressively as Python, you would rarely force an exception to happen if you could avoid it by a quick test.

So which of these should be recommended? Incidentally, for the sake of this discussion, please imagine my throw statement to be your preferred way of exiting a function with extreme prejudice.

Avoid the error:
if(Test-Path $ConfigurationFile) {
    $Config = Get-Content $ConfigurationFile
} else {
    # We could write a warning and return, but for consistency:
    throw "You must configure MyModule first, please call Initialize-MyModule"
}
<# Do the work #>

Of course, if it's best practice to avoid errors when possible, you still have to have a best practice for dealing with them, because it's not always as easy as Test-Path to avoid them.

Suppress the error and check the output:
if($Config = Get-Content $ConfigurationFile -ErrorAction SilentlyContinue) {
    <# Do the work #>
}
# We could have just done ... nothing, but for consistency:
throw "You have to configure MyModule first, please call Initialize-MyModule"

Or I could write that backwards:

if(!($Config = Get-Content $ConfigurationFile -ErrorAction SilentlyContinue)) {
    throw "You have to configure MyModule first, please call Initialize-MyModule"
}
<# Do the work #>
Force an exception and catch it
try {
    $Config = Get-Content $ConfigurationFile -ErrorAction Stop
} catch { 
    # Normally you'd be using some information from the exception, but for consistency
    throw "You have to configure MyModule first, please call Initialize-MyModule"
}
<# Do the work #>
Deal with the error itself
$Config = Get-Content $ConfigurationFile -ErrorAction SilentlyContinue -ErrorVariable NoConfig

if($NoConfig) {
    # Normally you'd be using some information from the error, but for consistency
    throw "You should configure MyModule first, please call Initialize-MyModule"
}
<# Do the work #>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions