Search code examples
powershellerror-handlingcommand

Why do proxy commands handle errors differently


For a while, I am maintaining a PowerShell Join-Object cmdlet.
In here I am creating a few proxy commands with default parameters, as FullJoin-Object, Merge-Object and Insert-Object as described here: Proxy Functions: Spice Up Your PowerShell Core Cmdlets.
(In earlier version I was using aliases which could problems if the user creates its own aliases.)

Everything works as expected except that the error handling differs a little between the main command and the proxy command...

Taken the following MVCE, based on the following function:

Function Inverse([Int]$Number) {
    Rubbish
    Write-Output (1 / $Number)
}

(Where the function Rubbish doesn't exist)

Than I create a proxy function called Reverse0:

$MetaData = [System.Management.Automation.CommandMetadata](Get-Command Inverse)
$Value = [System.Management.Automation.ProxyCommand]::Create($MetaData)
$Null = New-Item -Path Function:\ -Name "Script:Inverse0" -Value $Value -Force
$PSDefaultParameterValues['Inverse0:Number'] = 0    # (Not really required for reproducing the issue)

If I run the original function Reverse 0, I get two errors:

Rubbish : The term 'Rubbish' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:2 char:1
+ Rubbish
+ ~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Rubbish:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Attempted to divide by zero.
At line:3 char:1
+ Write-Output (1 / $Number)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException

If run the proxy command Reverse0 (or Reverse0 0), I get only the first error:

Rubbish : The term 'Rubbish' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:2 char:1
+ Rubbish
+ ~~~~~~~
+ CategoryInfo          : ObjectNotFound: (Rubbish:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException

It seems that something like the $ErrorActionPreference has changed but I checked that and it appears to be the same within both functions.

Is there and explanation for the different behavior?
Is there a way to get a Proxy Command act the same as the original command with respect to error handling?


Solution

  • The code that [System.Management.Automation.ProxyCommand]::Create() generates is currently (PowerShell Core 7.0.0-preview.5) flawed:

    It incorrectly propagates statement-terminating errors as script-terminating errors, by using throw rather than $PSCmdlet.ThrowTerminatingError($_) in its catch blocks, causing the function to abort instantly.

    If you manually correct these calls, your proxy function should behave as expected.

    See GitHub issue #10863; the linked issue isn't specifically about this behavior, but a change has been green-lighted, and it should include a fix for it.

    In concrete terms, for now, you'll have to fix the generated code manually:

    • Locate all try / catch statements that look like this:
        try {
            # ...
        } catch {
            throw
        }
    
    • and replace them with:
        try {
            # ...
        } catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }