PowerShell live documentation

Versions tested: 2.0.0, 5.1.1007, 6.0.0, 6.0.1, 6.0.2, 6.0.3, 6.0.4, 6.0.5, 6.1.0, 6.1.1, 6.1.2, 6.1.3, 6.1.4, 6.1.5, 6.1.6, 6.2.0, 6.2.1, 6.2.2, 6.2.3, 6.2.4, 6.2.5, 7.0.0, 7.0.1

Variable $? in parentheses

In PowerShell, the $? variable represents the exit status of the previous command. If it's true, the command succeeded. If it's false, the command failed. However, you need to be careful if using the variable, since enclosing a command in parentheses can reset $? to true in PowerShell 6 and earlier.

$local:ErrorActionPreference = "SilentlyContinue"

Write-Error error
Write-Output "outside of parentheses: `$? = $?"

(Write-Error error)
Write-Output "inside of parentheses: `$? = $?"
Output by version
2.x, 5.x, 6.x 7.x
outside of parentheses: $? = False
inside of parentheses: $? = True
outside of parentheses: $? = False
inside of parentheses: $? = False

The WriteError function

When $ErrorActionPreference is Stop, $PSCmdlet.WriteError exits the current advanced function and throws. However, the exception it throws is not catchable from within the advanced function.

It's worth noting that in PowerShell 2, the exception is caught both inside the function and outside the function. Inside the function we catch a "The pipeline has been stopped." error, and then continue execution within the function. When the function exits, we then throw the original exception.

function TestWriteErrorFunction {
    [CmdletBinding()]
    param()

    $local:ErrorActionPreference = "Stop"
    try {
        $PSCmdlet.WriteError((NewErrorRecord "error in try"))
    } catch {
        Write-Output "caught inside the function: $_"
    }

    Write-Output "after the try-catch"
}

try {
    TestWriteErrorFunction
} catch {
    Write-Output "caught outside the function: $_"
}
Output by version
2.x 5.x, 6.x, 7.x
caught inside the function: The pipeline has been stopped.
after the try-catch
caught outside the function: error in try
caught outside the function: error in try

Write-Error, on the other hand, is catchable inside the function, for both advanced and basic functions.

function TestWriteErrorCmdletAdvanced {
    [CmdletBinding()]
    param()

    $local:ErrorActionPreference = "Stop"
    try {
        Write-Error "error in try"
    } catch {
        Write-Output "caught inside the function: $_"
    }

    Write-Output "after the try-catch"
}

function TestWriteErrorCmdletBasic {
    param()

    $local:ErrorActionPreference = "Stop"
    try {
        Write-Error "error in try"
    } catch {
        Write-Output "caught inside the function: $_"
    }

    Write-Output "after the try-catch"
}

Write-Output "testing advanced function"
try {
    TestWriteErrorCmdletAdvanced
} catch {
    Write-Output "caught outside the function: $_"
}

Write-Output ""

Write-Output "testing basic function"
try {
    TestWriteErrorCmdletBasic
} catch {
    Write-Output "caught outside the function: $_"
}
Output by version
2.x, 5.x, 6.x, 7.x
testing advanced function
caught inside the function: error in try
after the try-catch

testing basic function
caught inside the function: error in try
after the try-catch

The WriteError function differs from Write-Error in another way as well. When used inside a function, either advanced or basic, Write-Error will not set $? to false after the function exits. WriteError, on the other hand, will set $? to false after the function exits.

Interestingly, Write-Error will set $? to false within its own scope. WriteError, however, won't touch $? until the function exits.

function AdvancedWriteErrorCmdlet {
    [CmdletBinding()]
    param()

    $local:ErrorActionPreference = "SilentlyContinue"
    Write-Error "an error"

    Write-Output "Inside advanced function calling Write-Error status: $?"
}

function BasicWriteErrorCmdlet {
    param()

    $local:ErrorActionPreference = "SilentlyContinue"
    Write-Error "an error"

    Write-Output "Inside basic function calling Write-Error status: $?"
}

function AdvancedWriteErrorFunction {
    [CmdletBinding()]
    param()

    $local:ErrorActionPreference = "SilentlyContinue"
    $PSCmdlet.WriteError((NewErrorRecord "an error"))

    Write-Output "Inside advanced function calling `$PSCmdlet.WriteError status: $?"
}

AdvancedWriteErrorCmdlet
Write-Output "Advanced function calling Write-Error exited with: $?"
Write-Output ""

BasicWriteErrorCmdlet
Write-Output "Basic function calling Write-Error exited with: $?"
Write-Output ""

AdvancedWriteErrorFunction
Write-Output "Advanced function calling `$PSCmdlet.WriteError exited with: $?"
Output by version
2.x, 5.x, 6.x, 7.x
Inside advanced function calling Write-Error status: False
Advanced function calling Write-Error exited with: True

Inside basic function calling Write-Error status: False
Basic function calling Write-Error exited with: True

Inside advanced function calling $PSCmdlet.WriteError status: True
Advanced function calling $PSCmdlet.WriteError exited with: False