Skip to content

Suppressing stderr output with 2>$null or redirecting it to a file causes stderr lines to be recorded in $Error #11133

Closed

Description

Closely related: #4002; see also: #4332

By design, stderr output from external programs is not recorded in $Error by default.

Paradoxically, when you use 2>$null in order to explicitly suppress stderr output, it then is recorded in $Error, which is obviously not the intent.

Note: It also happens when you redirect to an actual file.

Steps to reproduce

$Error.Clear()
# Call `whoami` with an invalid argument, which produces stderr output (all platforms)
whoami badarg 2>$null
$Error.Count | Should -Be 0

Expected behavior

The test should succeed.

Actual behavior

The test fails with something like:

Expected 0, but got 1

That is, stderr output was unexpectedly recorded in $Error.

Environment data

PowerShell Core 7.0.0-preview.5

Activity

  1. added
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    on Nov 20, 2019
  2. iSazonov commented on Nov 20, 2019

    @iSazonov
    Collaborator

    I think it is expected as -ErrorAction SilentlyContinue behavior.

  3. mklement0 commented on Nov 20, 2019

    @mklement0
    ContributorAuthor

    Ilya (@iSazonov):

    • Stderr output doesn't belong in PowerShell's error stream, because it cannot be assumed to represent errors - that's why it prints straight to the host by default.

    • Using 2>$null should mean: discard stderr output. That it would then show up in $Error is certainly not the intent.

    • That stderr output goes through the error stream with redirection 2> is also highly problematic with respect to suddenly being subject to $ErrorActionPreference = 'Stop' - see Redirecting stderr / the error stream can trigger script-terminating errors #4002.

    Providing an easy way to collect stderr lines is the subject of #4332.

  4. BrucePay commented on Nov 21, 2019

    @BrucePay
    Collaborator

    Michael Klement (@mklement0) Logically standard error output belongs in PowerShell's output stream because because that's where executables write their errors. Unfortunately over the years more and more *NIX binaries use stderr for a bunch of things that have nothing to do with errors. This is annoying and unfortunate. PowerShell's output stream is strongly typed so whatever gets written into it has to get wrapped in an ErrorRecord unlike the subprocess pipe which is just an undifferentiated stream of bytes so you get very different behavior. (Wrapping the output into an error object is also important because that's how Out-Default demultiples the merged output stream.)

    That it would then show up in $Error is certainly not the intent.

    It most certainly is the intent, specifically $error is intended to be an in-memory log of all the errors that occur during execution, suppressed or not. It's purpose, like any error log, is to allow forensic analysis of the execution.

    That stderr output goes through the error stream with redirection 2> is also highly problematic

    I didn't understand this statement. WRT the linked issue, errors are either exceptions (terminating errors) or something written to the error stream (non-terminating errors.) -ErrorAction Stop changes a non-terminating error into a terminating error. Thus error action only applies to things being written to the error stream.. Consequently, if you redirect the error stream to the output stream, the error action preference no longer has any effect.

  5. mklement0 commented on Nov 22, 2019

    @mklement0
    ContributorAuthor

    Bruce Payette (@BrucePay):

    Unfortunately over the years more and more *NIX binaries use stderr for a bunch of things that have nothing to do with errors. This is annoying and unfortunate.

    The root problem is that native programs only have 2 output streams at their disposal: stdout and stderr.
    This is an unfortunate limitation, which PowerShell - commendably - has overcome in its own realm.

    Given this limitation, native programs have no choice but to write anything that isn't data to stderr, lest they pollute the data output stream (stdout).
    It is a longstanding practice that is the best approach within the confines of the stated limitation.

    Because the presence of stderr output can therefore not be assumed to represent error conditions, PowerShell's console host's default behavior is the right approach:

    • print stderr lines to the host
    • do not record stderr lines in $Error

    If needed, stderr output can be targeted selectively with a 2> redirection; currently, aside from 2>$null discussed below, you can only redirect to a file; it would be nice to have an analog to -ErrorVariable for capturing stderr lines in a variable, which is what #4332 proposes.


    It most certainly is the intent, specifically $error is intended to be an in-memory log of all the errors that occur during execution, suppressed or not.

    As explained above, for good reasons that doesn't apply to stderr output, which is passed through to the host.

    That it would suddenly apply when you use 2>$null to explicitly signal the intent to discard stderr can't be anyone's intent, least of all the user's.


    Thus error action only applies to things being written to the error stream..

    Not only should 2>$null not touch the error stream at all, that a redirection designed to discard the stream's output, would - situationally, based on $ErrorActionPreference - trigger aborting execution as if it were writing to the error stream is obviously the opposite of the user's intent.

    Even with true error stream output from PowerShell commands 2>$null is currently broken:

    • Like -ErrorAction SilentlyContinue it still records the error in $Error (which I think is problematic - see below).

    • Unlike -ErrorAction SilentlyContinue (or -ErrorAction Ignore), it unexpectedly throws a runspace-terminating error when $ErrorActionPreference = 'Stop' happens to be in effect:

    $ErrorActionPreference = 'Stop'
    
    # OK: No error output, but $Error is still populated.
    Get-Item /NoSuch -ErrorAction SilentlyContinue
    
    # Kaboom! A runspace-terminating error occurs.
    Get-Item /NoSuch 2>$null

    Even if 2>$null were made fully equivalent to -ErrorAction SilentlyContinue, it seems like the wrong way to go.

    The syntax 2>$null clearly suggests the intent to discard the error stream's output (as its native cousins 2>/dev/null and 2>NUL do), and should therefore be the equivalent of -ErrorAction Ignore.

  6. changed the title [-]Suppressing stderr output with 2>$null causes stderr lines to be recorded in $Error[/-] [+]Suppressing stderr output with 2>$null or redirecting it to a file causes stderr lines to be recorded in $Error[/+] on Feb 7, 2020
  7. mklement0 commented on Aug 2, 2020

    @mklement0
    ContributorAuthor

    Closed in favor of #3996 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions