The stderr stream
Note: On GitHub Actions runners (where this site was generated), all of the output is different than on my (benweedon's) machine. Issue 29 tracks this. Best not to fully trust anything on this page until we have this figured out.
Basic case
When ErrorActionPreference
is Continue
, stderr in PowerShell Core
behaves similarly to stderr in other shells. Most stderr output by
commands you call will be sent directly to the PowerShell process's own
stderr. It is interesting to note, however, that assigning the stderr
command to a variable, casting it to Void
, or piping it to Out-Null
results in the stderr stream being received by stdout.
In PowerShell 2 and 5, the result is much different. Not only are stack
traces printed out to stderr (except for the first print for some
reason), nothing is printed to stdout. Everything is printed to stderr
except redirecting to $null
or a file and the redirect version of the
variable, Void
, and Out-Null
cases.
Here is the output from writing directly to stderr. All versions print the same things, except that versions 2 and 5 have stack traces.
$local:ErrorActionPreference = 'Continue'
cmd /c 'echo printing to stderr 1 >&2'
cmd /c 'echo printing to stderr 2 >&2'
printing to stderr 1 cmd : printing to stderr 2 At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_4a44e277-2726-4bf0-bd52-ac5af02e4d1c\__script.ps1:5 char:16 + cmd <<<< /c 'echo printing to stderr 2 >&2' + CategoryInfo : NotSpecified: (printing to stderr 2 :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
printing to stderr 1 cmd : printing to stderr 2 At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_5296b3bd-8c95-4f21-863a-e7a1891e851a\__script.ps1:5 char:13 + cmd /c 'echo printing to stderr 2 >&2' + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (printing to stderr 2 :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
printing to stderr 1 printing to stderr 2
Here we are redirecting to stdout, $null
, and a file. Nothing prints
the $null
line. Versions 2 and 5 write the stdout line to stderr,
while PowerShell Core writes it to stdout. All versions correctly write
to the file, with versions 2 and 5 including stack traces.
$local:ErrorActionPreference = 'Continue'
cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
cmd /c 'echo redirecting stderr to $null >&2' 2> $null
cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
error.txt: cmd : redirecting stderr to a file At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_145b536b-6e5b-4bea-b422-7fb348a65cb6\__script.ps1:6 char:16 + cmd <<<< /c 'echo redirecting stderr to a file >&2' 2> error.txt + CategoryInfo : NotSpecified: (redirecting stderr to a file :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
error.txt: cmd : redirecting stderr to a file At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_6b2f74aa-67a6-489d-a779-b88ea5379993\__script.ps1:6 char:13 + cmd /c 'echo redirecting stderr to a file >&2' 2> error.t ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (redirecting stderr to a file :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
redirecting stderr to stdout error.txt: redirecting stderr to a file
cmd : redirecting stderr to stdout At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_145b536b-6e5b-4bea-b422-7fb348a65cb6\__script.ps1:4 char:16 + cmd <<<< /c 'echo redirecting stderr to stdout >&2' 2>&1 + CategoryInfo : NotSpecified: (redirecting stderr to stdout :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
cmd : redirecting stderr to stdout At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_6b2f74aa-67a6-489d-a779-b88ea5379993\__script.ps1:4 char:13 + cmd /c 'echo redirecting stderr to stdout >&2' 2>&1 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (redirecting stderr to stdout :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
And finally here we redirect to a variable, cast to Void
, and
redirect to Out-Null
. This test has huge variations between versions.
All versions handle variables the same, assigning stderr to the
variable if it's redirected and otherwise printing to stderr and
leaving the variable blank.
Then it gets kinda weird. Versions 6.x and 7.0.3 print the
non-redirected Void
cast to stdout, while everything else prints it
to stderr. The Out-Null
test is the same, except 7.0.2 joins the
6.x/7.0.3 group.
$local:ErrorActionPreference = 'Continue'
$v = cmd /c 'echo assigning command result to a variable >&2'
Write-Output "`$v: $v"
$v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
Write-Output "`$v: $v"
[Void] (cmd /c 'echo casting stderr command to Void >&2')
[Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
$v: $v: assigning redirected command result to a variable
$v: $v: assigning redirected command result to a variable casting stderr command to Void piping stderr command to Out-Null
$v: $v: assigning redirected command result to a variable piping stderr command to Out-Null
assigning command result to a variable cmd : casting stderr command to Void At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_28f5d03d-bea0-4a2f-b3e3-c052788d867a\__script.ps1:8 char:24 + [Void] (cmd <<<< /c 'echo casting stderr command to Void >&2') + CategoryInfo : NotSpecified: (casting stderr command to Void :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError cmd : piping stderr command to Out-Null At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_28f5d03d-bea0-4a2f-b3e3-c052788d867a\__script.ps1:10 char:16 + cmd <<<< /c 'echo piping stderr command to Out-Null >&2' | Out-Null + CategoryInfo : NotSpecified: (piping stderr command to Out-Null :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
assigning command result to a variable cmd : casting stderr command to Void At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_a0c00004-18b5-4f0c-b009-3ac37ec821e5\__script.ps1:8 char:21 + [Void] (cmd /c 'echo casting stderr command to Void >&2') + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (casting stderr command to Void :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError cmd : piping stderr command to Out-Null At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_a0c00004-18b5-4f0c-b009-3ac37ec821e5\__script.ps1:10 char:13 + cmd /c 'echo piping stderr command to Out-Null >&2' | Out ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (piping stderr command to Out-Null :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
assigning command result to a variable
assigning command result to a variable casting stderr command to Void
If you redirect stderr while ErrorActionPreference
is Stop
, an
exception is generated. Here we have the same groupings, first writing
directly to stderr. Since we aren't redirecting stderr, no exceptions
are thrown except in versions 2 and 5, which seem to like throwing the
second time you write to stderr for some reason.
$local:ErrorActionPreference = 'Stop'
try {
cmd /c 'echo printing to stderr 1 >&2'
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo printing to stderr 2 >&2'
} catch {
Write-Output "Caught: $_"
}
Caught: printing to stderr 2
printing to stderr 1
printing to stderr 1 printing to stderr 2
Then redirecting to stdout, $null
, or a file all cause exceptions.
$local:ErrorActionPreference = 'Stop'
try {
cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo redirecting stderr to $null >&2' 2> $null
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
} catch {
Write-Output "Caught: $_"
}
Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
Caught: redirecting stderr to stdout Caught: redirecting stderr to $null Caught: redirecting stderr to a file error.txt:
And finally redirecting to a variable, casting to Void
, or
redirecting to Out-Null
throw exceptions for all tests where stderr
is redirected to stdout (which makes sense, since redirecting to stdout
by itself throws).
For all versions, assigning to a variable without redirection leaves
the variable empty and prints to stderr. All other operations throw
except in version 7.0, where casting stderr to Void
prints to stderr
instead.
$local:ErrorActionPreference = 'Stop'
try {
$v = cmd /c 'echo assigning command result to a variable >&2'
Write-Output "`$v: $v"
} catch {
Write-Output "Caught: $_"
}
try {
$v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
Write-Output "`$v: $v"
} catch {
Write-Output "Caught: $_"
}
try {
[Void] (cmd /c 'echo casting stderr command to Void >&2')
} catch {
Write-Output "Caught: $_"
}
try {
[Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
} catch {
Write-Output "Caught: $_"
}
try {
cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
} catch {
Write-Output "Caught: $_"
}
$v: Caught: assigning redirected command result to a variable Caught: casting stderr command to Void Caught: casting redirected stderr command to Void Caught: piping stderr command to Out-Null Caught: piping redirected stderr command to Out-Null
$v: Caught: assigning redirected command result to a variable Caught: casting redirected stderr command to Void Caught: piping stderr command to Out-Null Caught: piping redirected stderr command to Out-Null
assigning command result to a variable
assigning command result to a variable casting stderr command to Void
Class methods
This behavior has interesting consequences for class methods (See
Method stdio). Since non-void methods automatically suppress
stdio, it's as if they were redirecting stderr to $null
, so printing
to stderr within a non-void method will produce an exception when
ErrorActionPreference
is Stop
.
Void
methods don't suppress stderr, even though they suppress stdout.
Despite this, Void
methods throw the first time stderr is printed in
version 5, and the second time in PowerShell Core.
$local:ErrorActionPreference = 'Stop'
class C {
[String] StringFunc() {
cmd /c 'echo StringFunc: printing to stderr 1 >&2'
cmd /c 'echo StringFunc: printing to stderr 2 >&2'
return 'some string'
}
[Void] VoidFunc() {
cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
}
}
$c = [C]::new()
try {
$c.StringFunc()
} catch {
Write-Output "Caught: $_"
}
try {
$c.VoidFunc()
} catch {
Write-Output "Caught: $_"
}
Caught: StringFunc: printing to stderr 1 Caught: VoidFunc: printing to stderr 1
Caught: StringFunc: printing to stderr 1 Caught: VoidFunc: printing to stderr 2
VoidFunc: printing to stderr 1
When ErrorActionPreference
is Continue
, I'm honestly not sure
what's going on. For GitHub Actions runners, it seems the Void
function prints to stdout in PowerShell Core and not in version 5. The
second Void
print makes it to stderr on its own in version 5, but all
other prints starting with "Note" seem to be compressed onto a single
line.
$local:ErrorActionPreference = 'Continue'
class C {
[String] StringFunc() {
cmd /c 'echo Note: This line only seems to print on GitHub Actions >&2'
cmd /c 'echo StringFunc: printing to stderr 1 >&2'
cmd /c 'echo StringFunc: printing to stderr 2 >&2'
return 'some string'
}
[Void] VoidFunc() {
cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
}
}
$c = [C]::new()
try {
$c.StringFunc()
} catch {
Write-Output "Caught: $_"
}
try {
$c.VoidFunc()
} catch {
Write-Output "Caught: $_"
}
some string
some string VoidFunc: printing to stderr 1 VoidFunc: printing to stderr 2
Note: This line only seems to print on GitHub Actions StringFunc: printing to stderr 1 StringFunc: printing to stderr 2 cmd : VoidFunc: printing to stderr 1 At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_79bc3a10-c2a6-43cf-845b-0392cc93223f\__script.ps1:13 char:21 + cmd /c 'echo VoidFunc: printing to stderr 1 >&2' + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (VoidFunc: printing to stderr 1 :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError cmd : VoidFunc: printing to stderr 2 At C:\Users\runneradmin\AppData\Local\Temp\pwsh-live-doc_79bc3a10-c2a6-43cf-845b-0392cc93223f\__script.ps1:14 char:21 + cmd /c 'echo VoidFunc: printing to stderr 2 >&2' + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (VoidFunc: printing to stderr 2 :String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
Note: This line only seems to print on GitHub Actions StringFunc: printing to stderr 1 StringFunc: printing to stderr 2
Note: This line only seems to print on GitHub Actions StringFunc: printing to stderr 1 StringFunc: printing to stderr 2 VoidFunc: printing to stderr 1 VoidFunc: printing to stderr 2
See also
This PowerShellTraps page is also an incredible source for information about stderr.