3

In order to be informed when PowerShell Startup / Logon scripts running on remote computers have bugs, I tend to end scripts with the following:

If ($Error) {
    (Code that sends a notification email to system administrators attaching the contents of the $Error variable for troubleshooting)
}

This is a great 'tell tale' to pick up edge cases / bugs. However, I've found some basic built-in PowerShell cmdlets dump data into $Error even on successful runs - for example, try:

$Error.Clear()
Get-NetIPConfiguration
$Error

And you'll see a load of errors in $Error that are not shown during normal output but look like:

Get-NetRoute : No matching MSFT_NetRoute objects found by CIM query for instances of the ROOT/StandardCimv2/MSFT_NetRoute class on the CIM server: SELECT * FROM MSFT_NetRoute WHERE ((DestinationPrefix LIKE '0.0.0.0/0')) AND ((InterfaceAlias LIKE 'OpenVPN Wintun')). Verify query parameters and retry.

Get-NetConnectionProfile : No MSFT_NetConnectionProfile objects found with property 'InterfaceAlias' equal to 'Local Area Connection'. Verify the value of the property and
retry.

or

$Error.Clear()
Get-NetIPAddress
$Error

will return:

“Infinite : The term '“Infinite' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

(A nice little bug for Microsoft to solve at some point, doubtless!)

Since it's unlikely that the cmdlets will be fixed any time soon, is there a way to run these cmdlets without them clogging up $Error with their useless information?

This is not a duplicate of Powershell: How can I stop errors from being displayed in a script? since that covers errors that actually display in red on the PowerShell console during a 'failed' run of the cmdlet; this is about errors generated by some cmdlets in the background during an apparently 'successful' run of a cmdlet which for some reason only get written to the automatic $Error variable.

Nonetheless I have already tried a number of solutions suggested in that post:

  • Running the cmdlets with -ErrorAction Ignore
  • Running the cmdlets with -ErrorAction SilentlyContinue
  • Running the cmdlets inside try {} catch {}
  • Running the cmdlets inside try {} catch {} with -ErrorAction Stop
  • Running the cmdlets with 2>$null following them
  • Setting $ErrorActionPreference = "SilentlyContinue" before running the cmdlets

I may be asking the impossible, but the way these cmdlets behave does make $Error very hard to use as an actual log, just want to know if I'm missing a trick.

I would like to be able to encapsulate buggy cmdlets in such a way that 'hidden' errors do not go into the automatic $Error variable.

Minkus
  • 335
  • 2
  • 13
  • 1
    I think you can't really prevent cmdlets from adding to `$Error`, if they don't play to the rules. The only thing you can do is to backup/restore the `$Error` variable. `$oldError = $Error.Clone(); MisbehavingCmdlet; $Error.Clear(); $Error.AddRange($oldError)` – zett42 Feb 03 '23 at 13:28
  • Anyone interested in getting the underlying PowerShell cmdlets fixed can upvote the Get-NetIPConfiguration bug here: https://aka.ms/AAjlt18 – Minkus Feb 09 '23 at 11:40
  • And the Get-NetIPAddress bug here: https://aka.ms/AAjlt2y – Minkus Feb 09 '23 at 12:14
  • Thanks, but I think it will be better to create issues at the PowerShell GitHub repository: https://github.com/PowerShell/PowerShell/issues – zett42 Feb 09 '23 at 13:29
  • 1
    Done this for Get-NetIPConfiguration, but the Get-NetIPAddress bug appears to be Windows PowerShell only, which the GitHub repo doesn’t cover https://github.com/PowerShell/PowerShell/issues/19125 – Minkus Feb 10 '23 at 09:05

2 Answers2

2

I agree with @zett42' comment: I think you can't really prevent cmdlets from adding to $Error.
Also knowing that these "phantom errors" might already occur with a simple (Try/Catch) statement like:

Try { 1/0 } Catch {}

Anyways, you might consider to mark the last one and remove the errors added after that/ Like:

$HashCode = if ($Error) { $Error[0].GetHashCode() }
Get-NetIPAddress
While($Error -and $Error[0].GetHashCode() -ne $HashCode) { $Error.RemoveAt(0) }
iRon
  • 20,463
  • 10
  • 53
  • 79
  • Thanks - this is really helpful, and a nice clean way to resolve! However, just tried this and it won't remove 'silent' errors if $Error starts out as empty (since there's no $HashCode so Error.RemoveAt(0) never runs). Should the third line be While ($Error ...) instead of While ($HashCode ...) ? – Minkus Feb 07 '23 at 14:00
  • 1
    I found this works just as well, and is shorter! `$HashCode = if ($Error) { $Error[0].GetHashCode() }; Get-NetIPAddress; While ($Error -and $Error[0].GetHashCode() -ne $LastErrorHash) { $Error.RemoveAt(0) };` – Minkus Feb 07 '23 at 14:44
  • That will work! (implemented in the answer) – iRon Feb 07 '23 at 14:57
0

Use the common -ErrorVariable parameter in order to collect only the (non-terminating) errors directly emitted or intentionally passed through by a cmdlet (those that it internally silences or ignores will not be captured):

# $errs is a self-chosen variable; note that it must be specified WITHOUT $
Get-NetIPAddress -ErrorVariable errs

# $errs now contains any (non-terminating) errors emitted by the 
# Get-NetIPAddress call, as a [System.Collections.ArrayList] instance.
# (If no errors occurred, the list is empty).

Note: To also silence errors, combine -ErrorVariable errs with -ErrorAction SilentlyContinue (-ErrorAction SilentlyContinue does not work - see below).


The automatic $Error variable is designed to provide a session-wide log of all errors.

However, (script) cmdlets that deliberately ignore errors can avoid logging unnecessary errors by using -ErrorAction Ignore in internal calls - assuming that the errors are not only to be silenced, but also needn't be inspected.
(If errors need to be inspected after having collected them with -ErrorVariable, use of -ErrorAction Ignore is not an option, because it prevents error collection.)

The CDXML-based cmdlets from the NetTCPIP module, such as Get-NetIPAddress unfortunately use -ErrorAction SilentlyContinue in cases where -ErrorAction Ignore would suffice.
Conceivably, the cmdlet-generation code predates v3 of PowerShell, when the Ignore value was introduced.

mklement0
  • 382,024
  • 64
  • 607
  • 775