PowerShell live documentation

The stderr stream

Basic case

When ErrorActionPreference is Continue, stderr in PowerShell 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 produces different results.

Here is the output from writing directly to stderr. All versions print both lines to stderr.

  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 
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_c05a1a9c-d014-498a-b13f-cec0b94c578c\__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_2fd36681-da4f-411b-adf6-7b1f172ce0a3\__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_c05a1a9c-d014-498a-b13f-cec0b94c578c\__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_2fd36681-da4f-411b-adf6-7b1f172ce0a3\__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. All PowerShell versions behave the same for these examples.

All versions assign stderr to the variable if it's redirected and otherwise print to stderr and leave the variable blank. The non-redirected Void and Out-Null cases print to stderr, and the redirected cases print nothing.

  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 
Stderr
assigning command result to a variable 
casting stderr command to Void 
piping stderr command to Out-Null 

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.

  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. }
Stderr
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 assigning to a variable, casting to Void, or redirecting to Out-Null throw exceptions for all cases where stderr is redirected to stdout (which makes sense, since redirecting to stdout by itself throws).

Assigning to a variable without redirection leaves the variable empty and prints to stderr. Casting stderr to Void and piping to Out-Null both print to stderr as well.

  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 redirected stderr command to Void 
Caught: piping redirected stderr command to Out-Null 
Stderr
assigning command result to a variable 
casting stderr command to Void 
piping stderr command to Out-Null 

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. So, calling VoidFunc will print to stderr.

  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 
Stderr
VoidFunc: printing to stderr 1 
VoidFunc: printing to stderr 2 

When ErrorActionPreference is Continue, StringFunc's return value gets printed to stdout (which makes sense, since it's returned), and it prints its other lines to stderr. VoidFunc prints both its lines to stderr.

What's interesting is that in GitHub Actions runners (where this site was generated), everything in StringFunc is printed to stderr without a newline after it. This results in the first line from VoidFunc being on the same line as all StringFunc output. On my (binyomen's) machine, nothing in StringFunc is printed at all. Since methods with return values should supress stdio, it seems like my machine has the "correct" behavior. This is, needless to say, extremely confusing, and is tracked by Issue 29.

  1. $local:ErrorActionPreference = 'Continue'
  2. class C {
  3.     [String] StringFunc() {
  4.         cmd /c 'echo Note: These lines only seem 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
Stderr
Note: These lines only seem 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.