Powershell: -ErrorAction Ignore doesn't work for calling advanced functions

Created on 12 Aug 2016  Â·  17Comments  Â·  Source: PowerShell/PowerShell

  1. Create a script with the following code:
function foo
{
    [CmdletBinding()]
    param()

process
{
    try
    {
        Write-Error 'boo'
    }
    finally { }
}

}

function bar
{
    foo -ErrorAction Ignore
}
  1. Dot-source that script to import the function definitions.
  2. Call the "bar" function.

Expected result:
Nothing (no output; no error--bar is calling foo with "-ErrorAction Ignore").

Actual result:
I get an error, and it's not even the one written by foo:

Write-Error : The value Ignore is not supported for an ActionPreference variable. The provided value should be used only as a value for a preference
parameter, and has been replaced by the default value. For more information, see the Help topic, "about_Preference_Variables."
At E:\temp\ps_ea_ignore_2.ps1:11 char:13
-             Write-Error 'boo'
-             ~~~~~~~~~~~~~~~~~
  - CategoryInfo          : NotSpecified: (:) [Write-Error], NotSupportedException
  - FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.WriteErrorCommand

Dan Thompson:
In addition to being annoying for straight-up calling advanced functions (you can't use "-ErrorAction Ignore" with them), this also hampers meta-programming (you can't do "& $someCmd -ErrorAction Ignore", because you might not know if $someCmd is an advanced function or not).

Resolution-Fixed WG-Engine

Most helpful comment

More specifically, I think what's going on is (disclaimer: haven't looked at the source code):

When an advanced function is called, the -ErrorAction _parameter value_ is translated into the $ErrorActionPreference _variable value_ in the scope of the function, presumably so as to propagate the setting to calls to other cmdlets made inside the same function.

The above is the cause of the problem: Ignore is not supported as a value of the preference _variable_, and the error message to that effect is triggered _whenever something inside the advanced function writes to the error stream_, including Write-Error itself.

A simplified example:

function Get-Foo {
    [CmdletBinding()] param ()
    # Output the function-scope $ErrorActionPreference variable's value.
    # NOTE: Only set if -ErrorAction ... was passed.
    (Get-Variable -Scope 0 ErrorActionPreference).Value
    # Trigger an error; if -ErrorAction Ignore was passed, this will
    # provoke the spurious "The value Ignore is not supported for an ActionPreference variable."
    # error.
    1 / 0
  }

Sourcing this function and invoking it as Get-Foo -ErrorAction Ignore shows the problem.

All 17 comments

This kinda stinks, but it's been there for a long time. I'm going to put it in the DevEx Project, and we can triage it against everything there.

More specifically, I think what's going on is (disclaimer: haven't looked at the source code):

When an advanced function is called, the -ErrorAction _parameter value_ is translated into the $ErrorActionPreference _variable value_ in the scope of the function, presumably so as to propagate the setting to calls to other cmdlets made inside the same function.

The above is the cause of the problem: Ignore is not supported as a value of the preference _variable_, and the error message to that effect is triggered _whenever something inside the advanced function writes to the error stream_, including Write-Error itself.

A simplified example:

function Get-Foo {
    [CmdletBinding()] param ()
    # Output the function-scope $ErrorActionPreference variable's value.
    # NOTE: Only set if -ErrorAction ... was passed.
    (Get-Variable -Scope 0 ErrorActionPreference).Value
    # Trigger an error; if -ErrorAction Ignore was passed, this will
    # provoke the spurious "The value Ignore is not supported for an ActionPreference variable."
    # error.
    1 / 0
  }

Sourcing this function and invoking it as Get-Foo -ErrorAction Ignore shows the problem.

…, but it's been there for a long time. …

Just because something was there for a long time
should never be an argument to decide a software development question.

It's just a question of a short time until any maintained Software gets a new release and in PowerShell, a Script can define the least expected runtime version.

So, it's no problem to let design errors disappear slowly and user-friendly :-)

Yes, it would be nice to get that fixed soon.

In the meantime, here is a workaround:

Place the following statement at the start [of the begin block] of your advanced function:

if ($ErrorActionPreference -eq 'Ignore') { 
  $ErrorActionPreference = 'Ignore' # create *local* variable with *string* value
}

This takes advantage of _another_ bug, #3483:
If you create a preference variable in a non-global scope, it isn't type-constrained, so the local $ErrorActionPreference instance truly contains the _string_ 'Ignore'.

Despite being a _string_, the value _does_ take effect and, because the check that causes the unwanted error expects an instance of the proper enumeration type, [System.Management.Automation.ActionPreference], the check is effectively _suppressed_.

The better workaround is to call $PSCmdlet.WriteError instead -- which is how binary cmdlets report errors.

I say it's strange insisting that one not use Ignore for the preference variable.

I say it's strange insisting that one not use Ignore for the preference variable.

Agreed.


The better workaround is to call $PSCmdlet.WriteError instead -- which is how binary cmdlets report errors.

It's better in the sense that it doesn't exploit _another_ bug the way that the above one-time execution of if ($ErrorActionPreference -eq 'Ignore') { $ErrorActionPreference = 'Ignore' } does, but it's certainly a lot more cumbersome:

Write-Error 'boo'

vs. the _roughly_ equivalent:

$PSCmdlet.WriteError(
    [Management.Automation.ErrorRecord]::new(
        'boo',
        $null,
        [Management.Automation.ErrorCategory]::NotSpecified,
        $null
    )
)

Exactly what I meant by "better". You get that working even if the other bug is fixed...

if ($ErrorActionPreference -eq 'Ignore') { $ErrorActionPreference = 'Ignore' }

Or we could use

$local:PSDefaultParameterValues = @{ 'ErrorAction' = $ErrorActionPreference.ToString() }

I was just reading the code behind this, and I have to ask: given how common parameters work in PowerShell today, setting action preference variables to ensure that the desired behavior propagates to other commands, isn't this a PSScriptAnalyzer problem that is inappropriately being handled internally within PowerShell code?

For reference, see this comment in ExecutionContext.cs.

https://github.com/PowerShell/PowerShell/blob/d80154430d600a52a1d1e3a9f3ecf032b36185d4/src/System.Management.Automation/engine/ExecutionContext.cs#L577-L578

That feels very wrong to me, and it gets in the way of being able to do something really useful (using -ErrorAction Ignore as appropriate when invoking advanced functions).

IMHO the best solution to this issue is to allow people to shoot themselves in the foot, but ensure that the tools they should be using (PSScriptAnalyzer directly or integrated into an editor like VS Code) properly flag that as red and identify that you should never set an action preference variable to Ignore, with proper guidance on how they should handle such things.

That solution fixes this issue, and is positioned to do a much better job educating the scripter so that they know better in the future.

Another use case - when running Pester tests it's useful to not have any output and let Pester handle the test case reporting.

Just adding to the chorus asking for a fix -- whatever the cause of the issue, the behavior makes no sense and hampers many scenarios. Please fix in PowerShell 6 and 7.

The fix for this was added to PowerShell 7 preview 4. This issue can be closed.

No 6 then? Whatever, I'll take it in 7 -- thanks!

Any changes to 6 at this point would only be made for security reasons.

yay

Still an issue in 7.1...

Name             : ConsoleHost
Version          : 7.1.0-preview.2
InstanceId       : d67d4374-65bd-4b90-b638-f51a4d71cd03
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-GB
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

@daverickdunn I'm not seeing it in 7.1preview2; can you share what you're doing that's making that show up for you?

Was this page helpful?
0 / 5 - 0 ratings