1

The output of Write-Error is ommited in methods that have a return value (i.e. are not void):

Class MyClass {
    [void]MethodWithoutReturnValue([string]$Msg) {
        Write-Error "MyClass.MethodWithoutReturnValue(): $Msg"
    }

    [bool]MethodWithReturnValue([string]$Msg) {
        Write-Error "MyClass.MethodWithReturnValue(): $Msg"
        $this.MethodWithoutReturnValue("this won't work either")
        return $true
    }
}

[MyClass]$obj = [MyClass]::new()
$obj.MethodWithoutReturnValue('this error will show up')
[bool]$Result = $obj.MethodWithReturnValue('this error will NOT show up')

I'm expecting three error message but only get one. Please notice that the calling the void method from the bool method also omits the output, as if the call stack is "poisoned" somehow. And yes (though not shown in this example) void method calling void method works.

Can someone please explain this behaviour or did I just find a bug?

  • 2
    ["In class methods, no objects get sent to the pipeline except those mentioned in the return statement. There's no accidental output to the pipeline from the code."](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_classes) `Write-Error` does attempt to interact with the pipeline and mixing in returned values can therefore well have unexpected effects. Classes are a bit of a red-headed stepchild in PowerShell at the moment anyway. Consider using exceptions instead (i.e. replace `Write-Error` with `throw`), which is the "proper .NET way" of generating errors. – Jeroen Mostert Jan 22 '20 at 22:48
  • As far as I know Write-Error does not write to the pipeline. Write-Output does. Regarding "just use throw instead" see my comment to @SagePourpres answer. – Jack Charon Jan 23 '20 at 15:05
  • PowerShell has the concept of streams associated with the pipeline; `Write-Output` writes to one such stream, `Write-Error` to another. We commonly only think of the pipeline as the stream that `Write-Output` writes to, but as the issue linked in the answer makes clear, the actual intent is definitely for no stream output to escape from class methods, including error output. Whether or not that's a design decision we have to like is another matter -- classes were originally added to PowerShell first and foremost to support DSC; they don't cooperate very smoothly with regular scripting. – Jeroen Mostert Jan 23 '20 at 16:22

1 Answers1

2

There's a bug open for that currently. The problem is actually that Write-Error work in void method.

By design, the intent is that you should use Throw to produce an error from within a class.

Here's the modified version of your script

Class MyClass {
    [void]MethodWithoutReturnValue([string]$Msg) {
       Throw "MyClass.MethodWithoutReturnValue(): $Msg"
    }

    [bool]MethodWithReturnValue([string]$Msg) {
       Throw "MyClass.MethodWithReturnValue(): $Msg"
        $this.MethodWithoutReturnValue("this won't work either")
        return $true
    }
}

[MyClass]$obj = [MyClass]::new()
$obj.MethodWithoutReturnValue('this error will show up')
[bool]$Result = $obj.MethodWithReturnValue('this error will NOT show up')

Additional note

Using Throw will stop your script so the script won't proceed further. To prevent that, use a Try{}Catch{} statement.

[MyClass]$obj = [MyClass]::new()
Try {
    $obj.MethodWithoutReturnValue('this error will show up')
    }
Catch {
    Write-Error $_
}
[bool]$Result = $obj.MethodWithReturnValue('this error will NOT show up')

# I love Cyan
Write-Host 'Second error not being in a try catch mean this will not get printed' -ForegroundColor Cyan

Reference

Github -Write-Error fails to work in class methods that return a non-void value

Sage Pourpre
  • 9,932
  • 3
  • 27
  • 39
  • I accept your answer because it made clear that this is a bug. – Jack Charon Jan 23 '20 at 14:53
  • On the other hand I don't aggree with "just use throw instead of Write-Error". The latter just writes text to a steam, the first swipes your stack if not catched. These are fundemental different operations and not interchangeable. If you're curious: in my case it was a recoverable error but the script shouldn't be completely silent about it. – Jack Charon Jan 23 '20 at 15:03