Powershell: Redirecting stderr / the error stream can trigger script-terminating errors

Created on 13 Jun 2017  路  9Comments  路  Source: PowerShell/PowerShell

Closely related to #3996 and #11133

The current behavior of calling external programs _without_ stderr redirection is being discussed in the linked issue.

Either way, simply adding 2>$null or 2>&1 - indeed, _any_ redirection of stderr (2>...) - to a command shouldn't fundamentally change its error-handling behavior.

Note that when a host other than the console host is involved (e.g., when in the _ISE_ or when _remoting_), the difference described below does _not_ occur, but the behavior is fundamentally different (and presumably undesired - see #3996).

Note:

  • Even though the repro steps use external programs, PowerShell commands are equally affected (e.g., & { $ErrorActionPreference = 'stop'; Get-Item NosuchFile 2>$null } unexpectedly produces an error, whereas -ErrorAction SilentlyContinue would not).

  • Similarly, using 2> with an actual file as the target is also affected (e.g., & { $ErrorActionPreference = 'stop'; whoami badarg 2>stderr.txt } aborts, and stderr.txt is empty).

Steps to reproduce

On Unix, replace cmd /c with sh -c.

& { $ErrorActionPreference = 'stop'; cmd /c nosuch; $? }
& { $ErrorActionPreference = 'stop'; cmd /c nosuch 2>$null; $? }

Expected behavior

Both commands should fundamentally behave the same, which, based on the current default behavior in the console, is to _not_ report an error, with stderr output _passed through_ in the 1st command and stderr output getting _suppressed_ in the other.

'nosuch' is not recognized as an internal or external command,
operable program or batch file.
False

Actual behavior

  • The 1st command behaves as expected.
'nosuch' is not recognized as an internal or external command,
operable program or batch file.
False
  • The 2nd command _terminates the statement_ and outputs the _first stderr line only_ in _red_ (note that in _Windows PowerShell_ you get the usual, full error output):
'nosuch' is not recognized as an internal or external command,

The 2nd half of the error message, operable program or batch file., is missing.

Presumably, this happens because:

  • as usual, each individual stderr _line_ becomes its own [System.Management.Automation.ErrorRecord] instance,
  • and the very 1st line by itself triggers termination of the statement (in this particular case, the cmd error message happens to span _2_ lines).

Note that _any_ 2> redirection causes the symptom, such as 2>&1 or 2>err.txt.

Seemingly, the following happens:

  • Despite _redirection_ of the error stream, the stderr lines are apparently still _passing through_ it, as evidenced by them showing up in $Error afterwards.

  • With $ErrorActionPreference = 'stop' in effect, anything getting written to PowerShell's error stream instantly triggers a function/script-terminating error.

Again, note that without a redirection, stderr lines neither show up in $Error nor do they cause the script to be terminated based on $ErrorActionPreference = 'stop'.

Environment data

PowerShell Core v6.0.0-beta.4 on macOS 10.12.5
PowerShell Core v6.0.0-beta.4 on Ubuntu 16.04.2 LTS
PowerShell Core v6.0.0-beta.4 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.413 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Issue-Bug Resolution-Duplicate WG-Engine

Most helpful comment

Have just hit this myself. Calling an exe and redirecting the errorstream to a file (it doesn't actually contain an error, just additional info), and had been happily using it for a year or two. I turned ErrorActionPreference to "Stop" globally, and suddenly it fails, despite there not being any actual errors.

All 9 comments

I am able to repro this. This does look like inconsistent behavior. If the default out-putter is configured then the error message is just displayed. But if error is redirected to a pipe then it goes through MshCommandRuntime processing and ErrorActionPreference is effective, and the stop exception is thrown.

This is done here:
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L634

I disagree w/ expected behavior. I don't think the expected behavior of 2>$null is documented anywhere. It was an intentional edge-case while I was searching for potential extensions to redirection syntax. You can find other similar cases, e.g.

try {cmd /c whamo! 2>$()} catch { "Dang!" }

P.S. What @mklement0 is referring to as "the 2nd half of the message" is not "missing", or if so then I found it... It's not coming from the native command, it's a PowerShell ErrorRecord that get's it's own slot.

PS> $error.Clear()
PS> cmd /c whamo! 2>$()
PS> $error.Count
2
PS> $error[0]
operable program or batch file.
PS> $error[1]
cmd : 'whamo!' is not recognized as an internal or external command,
At line:1 char:1
+ cmd /c whamo! 2>$()
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: ('whamo!' is not...ternal command,:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

@BurtHarris I've revised the expected-behavior description in the original post in light of the discussion in the linked issue: if the consensus is that stderr output should never generate an error, then the expected behavior is as _now_ described in the initial post.

Yes, _any_ 2> redirection triggers the symptom, presumably for the reasons @PaulHigin states.

Without $ErrorActionPreference = 'Stop' in the mix, you do indeed get _all_ stderr lines recorded in $Error, as an _error record each_, but _with_ $ErrorActionPreference = 'Stop', the _1st_ line triggers termination of the pipeline, so the 2nd line never gets recorded in $Error.

I don't think the expected behavior of 2>$null is documented anywhere.

Indeed, about_Redirection doesn't mention redirecting to $null, but if it does anything other than _silently discarding the stream_ (as it does with the _success stream_, for instance), we have a problem - as evidenced by this issue.

The unfortunate interaction with $ErrorActionPreference = 'Stop' is one thing, but the fact that output that you're asking to have _suppressed_ is _still getting recorded_ somewhere - in $Error - is absolutely counter-intuitive.

Where is this issue? I've been able to repro it across an upgrade of a build version and wonder if I'm missing something since this is such an old thread.

repro: powershell v5.1.17763.316, OS 10.0.17763
no-repro: powershell v5.1.16299.967, OS 10.0.16299

Is the upgrade from 16299 to 17763 going to pull this in, and any projection of a fix? We've seen unreliability in our CI builds bailing out at odd times when they hit a machine with this newer powershell installed.

Hi.
Let me chime in.

I have a script which cheks if a python module is installed

python -c "import blah" 2>$null
if ($?) {
  # Run expensive installations
}

then I enabled strict error handling $ErrorActionPreference = "stop" and whenever that line runs (and the module is not setup), the whole script aborts instead of running the following steps.
I tried also with python -c ... 2>&1 | out-null and the NativeCommandError is always there.
This seems like a clear bug to me. If the error stream is being redirected to $null, that means that the user is explicitly ignoring any errors.
So far, the only work around is to use try{} catch{}.

Testcase

Write-Host You should not see an error
$ErrorActionPreference = "stop"
python -c "import notfound_foobar" 2>$null
Write-Host PASS

Have just hit this myself. Calling an exe and redirecting the errorstream to a file (it doesn't actually contain an error, just additional info), and had been happily using it for a year or two. I turned ErrorActionPreference to "Stop" globally, and suddenly it fails, despite there not being any actual errors.

Related to #3996, want to address similarly

We discussed this in the @PowerShell/powershell-committee and agreed this is a dupe of #3996

Was this page helpful?
0 / 5 - 0 ratings