5

I am hoping for a bit of help on an issue I'm having where once I add -ErrorAction SilentlyContinue to my command, no errors are written to the $Error variable. When I remove the ErrorAction, the command works perfectly fine and I can see the errors in the $Error variable but the errors are also printed to the console, which I don't want. Once I add the ErrorAction, the command still works but does not write any errors to the $Error variable.

Below is the function that contains the command.

function removebutton{
    Try{
        Write-Host "Please Wait..."
        Import-CSV $WPFfile_path.Text | foreach{Remove-DistributionGroupMember -Identity $WPFlist_email.Text -Member $_.email -Confirm:$false -ErrorAction SilentlyContinue}
        Write-Host("Completed Processing List")
        [System.Windows.MessageBox]::Show("Completed Processing List")       
            If ($error -ne $null) 
            {
            Write-Host -ForegroundColor Red ($Error | Select-Object Exception -ExpandProperty Exception | Out-String)
            [System.Windows.MessageBox]::Show(($Error | Select-Object Exception -ExpandProperty Exception | Out-String))
            }
            Else 
            {
            }
        Get-DistributiongroupMember $WPFlist_email.Text | Select-Object DisplayName, PrimarySMTPAddress | Out-GridView
        }
    Catch{
        [System.Windows.MessageBox]::Show(($Error[0]))
        }
        }

Any help will be greatly appreciated!

Kind regards, Dust

When I remove the ErrorAction, the command works perfectly fine and I can see the errors in the $Error variable but the errors are also printed to the console, which I don't want. Once I add the ErrorAction, the command still works but does not write any errors to the $Error variable.

1
  • 1
    Essentially, this command just doesn't add any errors to the $Error variable: Import-CSV $WPFfile_path.Text | foreach{Remove-DistributionGroupMember -Identity $WPFlist_email.Text -Member $_.email -Confirm:$false -ErrorAction SilentlyContinue} Commented Jan 8, 2023 at 1:41

3 Answers 3

8

Note:

  • The following describes PowerShell's normal behavior.
  • As it turns out, an apparent bug in Remove-DistributionGroupMember prevents non-terminating errors from being recorded in $Error with -ErrorAction SilentlyContinue present, which - for reasons unknown - can be bypassed by alternatively using 2>$null to silence error output instead.

  • -ErrorAction SilentlyContinue does record non-terminating errors in the automatic $Error variable (it is only -ErrorAction Ignore that doesn't).

    • Do not use If ($Error -ne $null) to test if an error occurred in the most recent statement, given that $Error contains all errors that have occurred in the session so far.

      • As an aside: To test a value for $null, place it on the LHS of -eq / -ne, given that these operators act as filters with collections (arrays) as the LHS - see the docs.
    • Instead, use the automatic $? variable: if (-not $?)

      • Alternatively, you could run $Error.Clear() before the statement of interest, which would then allow you to use if ($Error.Count -gt 0), but that comes at the expense of wiping out the session-level history of errors.

      • The best option may be to use the common -ErrorVariable parameter, which allows you to collect a given call's non-terminating errors in a self-chosen variable (which must be specified without $; e.g., -ErrorVariable errs in order to collect errors in $errs); note that -ErrorAction SilentlyContinue (or redirection 2>$null) is still also needed to prevent error output.

  • However, non-terminating errors - which is what the common -ErrorAction parameter exclusively acts on - do not trigger the catch block of try ... catch ... finally statements - only terminating errors do.

    • You can promote non-terminating errors to terminating errors by using -ErrorAction Stop, causing them to trigger the catch block too.
  • Note that, in a catch script block, you can more simply refer to the triggering error via the automatic $_ variable; that is, instead of $Error[0], you can use $_.


For a comprehensive overview of PowerShell's bewilderingly complex error handling, see GitHub docs issue #1583.

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you for your answer! The issue that I have is that this specific command is not writing to the $Error variable: Import-CSV $WPFfile_path.Text | foreach{Remove-DistributionGroupMember -Identity $WPFlist_email.Text -Member $_.email -Confirm:$false -ErrorAction SilentlyContinue} So when I try to "Write-Host" the $Error variable, nothing shows up.
@DustinEllse, assuming that the foreach (ForEach-Object) call receives at least one input object and Remove-DistributionGroupMember reports any non-terminating errors, they should be recorded in $Error. Therefore: (a) does the foreach call receive input and (b) where are you trying to use Write-Host?
Yes, foreach receives input. The command works fine and users are removed from the distribution list but if the user was not already part of the DL, the error should be written to the $Error variable, but it stays empty. The Write-Host is in an If statement that tells it to print the $Error variable if there's anything in it: If ($error -ne $null) {Write-Host -ForegroundColor Red ($Error | Select-Object Exception -ExpandProperty Exception | Out-String). I have also checked the $Error variable in the console and it's empty.
@DustinEllse, a simplified example: $Error.Clear(); try { Get-Item NoSuchFile -ErrorAction SilentlyContinue; Write-Verbose -Verbose $error[0] } catch { 'uh-oh' }. I'm only aware of two types of errors, non-terminating and terminating, the latter coming in the forms of statement-terminating and script-terminating (fatal) errors. In the example above, you should see either the Write-Verbose output (non-terminating error) or uh-oh (terminating error). Are you saying that with Remove-DistributionGroupMember you're seeing neither?
For further context, my initial command runs in a function that is called when clicking a button in a GUI that I've written. The command receives variables from text boxes for the location of a CSV file and the address of the DL. Without SilentlyContinue, the GUI works perfectly apart from the fact that it writes errors related to existing members to the console. It also writes to the $Errror variable with no issues. Once I've set it to SilentlyContinue, the $Error variable stays empty and I'm unable to set the -ErrorVariable too (also remains empty).
|
0

Amazingly, replacing -ErrorAction SilentlyContinue with 2>$null resolved my issue. The errors are no longer written to console and only written to the $Error variable.

Thank you @mklement0 for your help! Much appreciated! I have bought you a coffee to say thanks!

1 Comment

First things first: thank you for the coffee, much appreciated. Very curious that 2>$null made a difference and it would be good to know why, but I'm glad that it solved your problem. I've also updated my answer with a brief summary.
0

Redirecting to 2>$null doesn’t always suppress errors. For example, if you run the following command, it will capture the error and also display it in the console:

 $Error.Clear()
 Add-Type -Path 'NonExisting.dll' -ErrorAction Stop 2>$Null
 $Error[0]

Output:

Add-Type : Cannot bind parameter 'Path' to the target. Exception setting "Path": "Cannot find path 'C:\Users\Admin\NonExisting.dll' because it 
does not exist."
At line:2 char:21
+      Add-Type -Path 'NonExisting.dll' -ErrorAction Stop 2>$Null
+                     ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (:) [Add-Type], ParameterBindingException
    + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.AddTypeCommand
 
Add-Type : Cannot bind parameter 'Path' to the target. Exception setting "Path": "Cannot find path 'C:\Users\Admin\NonExisting.dll' because it 
does not exist."
At line:2 char:21
+      Add-Type -Path 'NonExisting.dll' -ErrorAction Stop 2>$Null
+                     ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (:) [Add-Type], ParameterBindingException
    + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.AddTypeCommand

You can capture the error and suppress the error message in the console by running it this way:

$Error.Clear()
.{Add-Type -Path 'NonExisting.dll' -ErrorAction Stop} 2>$Null
$Error[0]

Output:

Add-Type : Cannot bind parameter 'Path' to the target. Exception setting "Path": "Cannot find path 'C:\Users\Admin\NonExisting.dll' because it 
does not exist."
At line:2 char:21
+      Add-Type -Path 'NonExisting.dll' -ErrorAction Stop 2>$Null
+                     ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : WriteError: (:) [Add-Type], ParameterBindingException
    + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.AddTypeCommand

2 Comments

Your . { ... } 2>$null approach only works by accident: generally, it only works with statement-terminating errors (as well as non-terminating ones) inside { ... }, not also with script-(runspace-)terminating ones, which is what -ErrorAction Stop normally creates. However, -ErrorAction Stop is ineffective in your case, because a parameter-binding error (which is inherently statement-terminating) happened to preempt (stemming from -Path 'NonExisting.dll'). To catch and silence script-terminating errors, you need try / catch
To give an example where your approach fails: . { Get-Item NoSuch -ErrorAction Stop } 2>$null

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.