How to ignore expected PowerShell errors

Although it's generally preferred to handle all your errors properly, so that they never happen, sometimes it's not entirely possible to do so.

An example of this I ran across, is with the built in Get-Hotfix cmdlet. On some systems, the date field is not filled out for certain patches, as shown in the screenshot below:

This may not seem like an issue at the time, but if you check your $error variable after running get-hotfix, you'll see a bunch of errors that were just generated, related to this missing item - one per item.


Exception calling "Parse" with "1" argument(s): "String was not recognized as a valid DateTime."
At line:6 char:21
+                     [DateTime]::Parse($this.psBase.properties["InstalledOn"].Val ...
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FormatException

Again, this may not seem like an issue at first glance. The errors don't show themselves visibly while running the command, so who cares, right?

The problem is, depending on how you're using this command, where it's being called from, and how much you're relying on exit codes and the $error variable, it may still be causing a problem for your script.

Personally, I think that if you know an error is going to occur, and there's no straightforward way of truly suppressing it, and your calling process is somehow relying on items in the $error variable (as many of mine do), it makes sense to remove those "known" errors yourself.

Here's how you can do that:


#Get the current error count, before running the offending process
$preerrorcount = $error.count


#The offending function or cmdlet (replace with your own)
Get-HotFix

#Get the new error count, after running the offending process
$posterrorcount = $error.count

#if the posterrorcount differs from the preerrorcount, start removing each of the errors it generated
if($posterrorcount -ne $preerrorcount)
{
    #get the difference in the pre and post error counts
    $difference = $posterrorcount - $preerrorcount

    #Loop through once for each new error count, and remove the latest error in the $error variable.
    for($i = 0;$i -lt $difference;$i++)
    {
        $error.RemoveAt(0)
    }

}

You could place the above code into a function, which you simply call after the bad cmdlet or process.

Avoiding legitimate errors

What if you don't want all generated errors to be removed? There may be legitimate ones there, and you don't want to just remove those, and never know that they're occuring.

Well, now that we have the basic function above, we can improve it by checking various things during each loop. In this case, we can look inside the specific error item it's currently processing, and match up some known values, to determine if we really want to remove this or not.

Here's how:


#Known error details
$KnownException = 'String was not recognized as a valid DateTime.'

#Get the current error count, before running the offending process
$preerrorcount = $error.count


#The offending function or cmdlet (replace with your own)
Get-HotFix

#Get the new error count, after running the offending process
$posterrorcount = $error.count

#if the posterrorcount differs from the preerrorcount, start removing each of the errors it generated
if($posterrorcount -ne $preerrorcount)
{
    #get the difference in the pre and post error counts
    $difference = $posterrorcount - $preerrorcount

    #Loop through once for each new error count, and remove the latest error in the $error variable.
    for($i = 0;$i -lt $difference;$i++)
    {
        #Check if the basic error message matches the one you entered as the known exception, if so, remove it, if not, leave it alone.
        if($error[0].Exception.InnerException.Message.ToLower() -eq $KnownException.ToLower())
        {
            $error.RemoveAt(0)
        }
    }

}

Putting it all together into a useful function

You don't want to be copying and modifying this script over and over again for each error you want to clean up, so it's easiest to put all this into a function that you can reuse, like so:


function Remove-KnownErrors
{
    param(
    [Parameter(Mandatory=$false,ParameterSetName="CompareException")]
    [ValidateNotNullOrEmpty()]
    [string]$KnownException,

    [Parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [int]$PreErrorCount,

    [Parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [int]$PostErrorCount

    )


    #if the posterrorcount differs from the preerrorcount, start removing each of the errors it generated
    if($posterrorcount -ne $preerrorcount)
    {
        #get the difference in the pre and post error counts
        $difference = $posterrorcount - $preerrorcount

        #Loop through once for each new error count, and remove the latest error in the $error variable.
        for($i = 0;$i -lt $difference;$i++)
        {
            #Check if the user entered a known exception or not. If they did, compare the exception message, if they didn't, just remove the current error item.
            if($KnownException)
            {
                #Check if the basic error message matches the one you entered as the known exception, if so, remove it, if not, leave it alone.
                if($error[0].Exception.InnerException.Message.ToLower() -eq $KnownException.ToLower())
                {
                    $error.RemoveAt(0)
                }
            }
            else
            {
                #Remove any error
                $error.RemoveAt(0)
            }

        }

    }

}



    #Get the current error count, before running the offending process
    $preerrorcount = $error.count


    #The offending function or cmdlet (replace with your own)
    Get-HotFix


    #Run the function, with appropriate inputs
    Remove-KnownErrors -PreErrorCount $preerrorcount -PostErrorCount $error.count -KnownException "String was not recognized as a valid DateTime."

The function can be placed in another file if you want, and then dot-sourced into any script, making the function available anywhere.