PowerShell live documentation

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.

  1. $local:ErrorActionPreference = 'Continue'
  2. cmd /c 'echo printing to stderr 1 >&2'
  3. cmd /c 'echo printing to stderr 2 >&2'
Stderr
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.

  1. $local:ErrorActionPreference = 'Continue'
  2. cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
  3. cmd /c 'echo redirecting stderr to $null >&2' 2> $null
  4. cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
  5. Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
Stdout
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 
Stderr
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.

  1. $local:ErrorActionPreference = 'Continue'
  2. $v = cmd /c 'echo assigning command result to a variable >&2'
  3. Write-Output "`$v: $v"
  4. $v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
  5. Write-Output "`$v: $v"
  6. [Void] (cmd /c 'echo casting stderr command to Void >&2')
  7. [Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
  8. cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
  9. cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
Stdout
$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 
Stderr
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.

  1. $local:ErrorActionPreference = 'Stop'
  2. try {
  3.     cmd /c 'echo printing to stderr 1 >&2'
  4. } catch {
  5.     Write-Output "Caught: $_"
  6. }
  7. try {
  8.     cmd /c 'echo printing to stderr 2 >&2'
  9. } catch {
  10.     Write-Output "Caught: $_"
  11. }
Stdout
Caught: printing to stderr 2 
Stderr
printing to stderr 1 
printing to stderr 1 
printing to stderr 2 

Then redirecting to stdout, $null, or a file all cause exceptions.

  1. $local:ErrorActionPreference = 'Stop'
  2. try {
  3.     cmd /c 'echo redirecting stderr to stdout >&2' 2>&1
  4. } catch {
  5.     Write-Output "Caught: $_"
  6. }
  7. try {
  8.     cmd /c 'echo redirecting stderr to $null >&2' 2> $null
  9. } catch {
  10.     Write-Output "Caught: $_"
  11. }
  12. try {
  13.     cmd /c 'echo redirecting stderr to a file >&2' 2> error.txt
  14. } catch {
  15.     Write-Output "Caught: $_"
  16. }
  17. Write-Output "error.txt: $((Get-Content error.txt) -join `"`n`")"
Stdout
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.

  1. $local:ErrorActionPreference = 'Stop'
  2. try {
  3.     $v = cmd /c 'echo assigning command result to a variable >&2'
  4.     Write-Output "`$v: $v"
  5. } catch {
  6.     Write-Output "Caught: $_"
  7. }
  8. try {
  9.     $v = cmd /c 'echo assigning redirected command result to a variable >&2' 2>&1
  10.     Write-Output "`$v: $v"
  11. } catch {
  12.     Write-Output "Caught: $_"
  13. }
  14. try {
  15.     [Void] (cmd /c 'echo casting stderr command to Void >&2')
  16. } catch {
  17.     Write-Output "Caught: $_"
  18. }
  19. try {
  20.     [Void] (cmd /c 'echo casting redirected stderr command to Void >&2' 2>&1)
  21. } catch {
  22.     Write-Output "Caught: $_"
  23. }
  24. try {
  25.     cmd /c 'echo piping stderr command to Out-Null >&2' | Out-Null
  26. } catch {
  27.     Write-Output "Caught: $_"
  28. }
  29. try {
  30.     cmd /c 'echo piping redirected stderr command to Out-Null >&2' 2>&1 | Out-Null
  31. } catch {
  32.     Write-Output "Caught: $_"
  33. }
Stdout
$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 
Stderr
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.

  1. $local:ErrorActionPreference = 'Stop'
  2. class C {
  3.     [String] StringFunc() {
  4.         cmd /c 'echo StringFunc: printing to stderr 1 >&2'
  5.         cmd /c 'echo StringFunc: printing to stderr 2 >&2'
  6.         return 'some string'
  7.     }
  8.     [Void] VoidFunc() {
  9.         cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
  10.         cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
  11.     }
  12. }
  13. $c = [C]::new()
  14. try {
  15.     $c.StringFunc()
  16. } catch {
  17.     Write-Output "Caught: $_"
  18. }
  19. try {
  20.     $c.VoidFunc()
  21. } catch {
  22.     Write-Output "Caught: $_"
  23. }
Stdout
Caught: StringFunc: printing to stderr 1 
Caught: VoidFunc: printing to stderr 1 
Caught: StringFunc: printing to stderr 1 
Caught: VoidFunc: printing to stderr 2 
Stderr
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.

  1. $local:ErrorActionPreference = 'Continue'
  2. class C {
  3.     [String] StringFunc() {
  4.         cmd /c 'echo Note: This line only seems to print on GitHub Actions >&2'
  5.         cmd /c 'echo StringFunc: printing to stderr 1 >&2'
  6.         cmd /c 'echo StringFunc: printing to stderr 2 >&2'
  7.         return 'some string'
  8.     }
  9.     [Void] VoidFunc() {
  10.         cmd /c 'echo VoidFunc: printing to stderr 1 >&2'
  11.         cmd /c 'echo VoidFunc: printing to stderr 2 >&2'
  12.     }
  13. }
  14. $c = [C]::new()
  15. try {
  16.     $c.StringFunc()
  17. } catch {
  18.     Write-Output "Caught: $_"
  19. }
  20. try {
  21.     $c.VoidFunc()
  22. } catch {
  23.     Write-Output "Caught: $_"
  24. }
Stdout
some string
some string
VoidFunc: printing to stderr 1 
VoidFunc: printing to stderr 2 
Stderr
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.