1

This is a follow up to my last question and just a request for clarification.

Because ThrowTerminatingError() will terminate the cmdlet as well as stop processing the items in a pipeline. What is the correct way to deal with 'problem' objects in a pipeline but you still want to continue processing the remaining items?

I'll use a very trivial example here, consider this function:

Function Do-Something
{

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True,
        ValueFromPipeline=$True)]
        [int]$value
    )

    process 
    {
        if($value -eq 3)
        {
            // Terminate if $value is 3.
        }
        Write-Output($value)
    }
}

Now assume this function is being invoked like so...

1..10 | Do-Something

Using throw:

if($value -eq 3)
{
    throw '$value was 3'
}

This is the output:

1
2
$value was 3
At C:\Users\Me\Desktop\Untitled1.ps1:51 char:12
+            throw '$value was 3'
+            ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: ($value was 3:String) [], RuntimeException
    + FullyQualifiedErrorId : $value was 3

Using ThrowTerminatingError():

if($value -eq 3)
{
    $PSCmdlet.ThrowTerminatingError((New-Object System.Management.Automation.ErrorRecord -ArgumentList (new-object System.Exception -ArgumentList ('$value was 3')), 'ValueWas3', 'NotSpecified', $value))       
}

Generates similar output:

1
2
Do-Something : $value was 3
At C:\Users\Me\Desktop\Untitled1.ps1:57 char:9
+ 1..10 | Do-Something
+         ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (3:Int32) [Do-Something], Exception
    + FullyQualifiedErrorId : ValueWas3,Do-Something

However if I use Write-Error and a return statement:

if($value -eq 3)
{
    Write-Error((New-Object System.Management.Automation.ErrorRecord -ArgumentList (new-object System.Exception -ArgumentList ('$value was 3')), 'ValueWas3', 'NotSpecified', $value))
    return
}

The items all process, reporting an error on the third one:

1
2
Do-Something : $value was 3
At C:\Users\Me\Desktop\Untitled1.ps1:58 char:9
+ 1..10 | Do-Something
+         ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException 
   ,Do-Something

4
5
6
7
8
9
10

Is this the correct way I should be dealing with problems with pipelined objects, or should I be looking at the structure of my functions?

Basically, I just want to know how should I deal with an object in a pipeline that can't be processed, but I want to continue to process any remaining ones.

The reason I'd want something like this is in the end block of a function I may sometimes want to report on which items were successfully processed and which failed (possibly with a reason).

Community
  • 1
  • 1
Jake
  • 1,701
  • 3
  • 23
  • 44
  • perhaps you should read up more on execption handling [C# MSDN Exceptions and Exception Handling](https://msdn.microsoft.com/en-us/library/ms173160.aspx) – MethodMan Oct 10 '15 at 19:33
  • I know how to handle exceptions, I'm just wondering how I'd go about allowing the remaning items to process since rethrowing the exception causes the pipeline to stop and the remaining items don't get processed. – Jake Oct 10 '15 at 19:37
  • 2
    don't rethrow it change the logic in the catch block to do some logging and then continue on with processing.. – MethodMan Oct 10 '15 at 19:40
  • So just using `return` to move to the next item is acceptable? – Jake Oct 10 '15 at 19:44
  • 1
    using `return` is fine but if you are logging or tracking the error prior to returning out of the method.. you should be fine.. – MethodMan Oct 10 '15 at 19:45
  • 1
    That's all I need to know then. Thanks. – Jake Oct 10 '15 at 19:46
  • no problem very well depicted example btw .. `+1` – MethodMan Oct 10 '15 at 19:47

1 Answers1

2

When writing a PowerShell command, you have two broad options for expressing errors: non-terminating and terminating errors.

Use a non-terminating error if the command can continue. For example, you're copying 1000 files and the first one is locked. There is no point in completely failing the entire operation. It is sufficient in this case to use Write-Error and write error info to the Error stream for that file and move on to copying the other files.

Use a terminating error when you encounter an error in which you just can't proceed. For example, if the first step of your command is to establish a connection to a remote service and the service is not responding, you can't really proceed. In the terminating error case, your options are: A) throw, B) use ThrowTerminatingError() or C) let an exception escape your command's implementation. These are variations of throwing (or not catching) an exception.

BTW whether or not you use the return statement after Write-Error depends on if you want to execute any more code (perhaps cleanup code) for the current pipeline object.

Keith Hill
  • 194,368
  • 42
  • 353
  • 369