How do I use trap statement in CmdLet without stopping pipeline processing?

Hello,

I'm lost as far as understanding how to implement error handling inside PROCESS{} block of cmdlet to allow processing the rest of pipeline if error occurs. Code below supposed to continue processing the rest of pipeline after first element is processed. If $ErrorActionPreference = "continue" then it works as expected but I need it to be "Stop" to be able to trap exception. What am I missing?

Set-StrictMode -Version Latest
$ErrorActionPreference = "stop"
Function Test
{
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true)]
[string[]]$filter
)
BEGIN {
trap {Write-Error $_}
}
PROCESS {
trap
{
Write-Error $_
Continue
}
Write-Output $_
1/0
}
END {

}
}
Here is output

PS C:\Users\g\Documents> "aa", "dd" | Test
aa
Attempted to divide by zero.
At :line:1 char:14
+ "aa", "dd" | T < <<< est

April 23rd, 2015 12:03pm

Write-Error will generate a non-terminating error.

Setting the ErrorActionPreference to stop forces PS to stop all execution (including of the whole script, not just inside your function) as soon as either a terminating or non-terminating error is caught.

You shouldn't really set the global ErrorAction to stop. Instead, run cmdlets (and advanced functions) with ErrorAction -Stop if you need to catch non-terminating errors.

For example:

# Uncaught non-terminating error 1/0

#Since error is non-terminating, rest of script still runs

try { # -ErrorAction stop forces errors from Get-Process to be treated as terminating, which allows me to catch it. Get-Process htoajhth -ErrorAction Stop } catch { 'I caught the error' } 'I still get executed'.




  • Edited by cogumel0 14 hours 50 minutes ago
Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 12:25pm

So, anytime I will use Write-Error entire script will terminate? I want to use single Trap{} statement inside my Process{} block of cmdlet which will capture any errors (terminating and not-terminating) inside Process{} block and then use Write-Error and continue with the rest of pipeline processing. How this can be done?
April 23rd, 2015 12:44pm

PowerShell has two types of errors:

Terminating errors

Non-terminating errors

By default, terminating errors will be written to the error stream and force a script to end immediately, whereas (again, by default), non-terminating errors will be written to the same error stream but allow the script to continue with its execution.

Most cmdlets generate non-terminating errors. The reason for this is simple. If I have the following command:

Get-Content file.txt | Get-Service

And someone remembers to write the 'helloWorld' on the very first line of that file, it will try to search for a service called 'helloWorld' and throw an error. Should this fail the whole script? Probably not. It should output a single error saying it failed on this service and continue to the next one. Imagine I wanted to do more meaningful things, like delete files, remove directories, etc.

If I have a list of files to delete and it fails on a single file, by default it will attempt to do the remaining ones.

This is the purpose of non-terminating errors.

If you have something that you REALLY must ensure is in a very particular state before continuing (you can't carry the next instruction unless you've confirmed a file is deleted for example), then you can force it to issue a terminating error, as such:

Remove-Item c:\windows\system32\format.com -ErrorAction Stop


If I run this from a non-elevated powershell window it will fail as I don't have permissions, but also stop the whole script from running.

Try..catch can't catch non-terminating errors, but if you put an -ErrorAction Stop at the end of the cmdlet you're trying to run, it will be 'transformed' into a terminating error and you can catch then catch it.

What you're trying to do will define what the best way to do it is. Rather than giving examples post your code so we can have a look at exactly what you're trying to do and find a way of giving you what you want.

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 1:25pm

Essentially I need code below to catch all errors, write to error stream and continue processing next input in PROCESS{} block of cmdlet

trap

{

Write-Error

continue

}

I provided example of what I want in original post. Can it be done?

April 23rd, 2015 1:32pm

Essentially I need code below to catch all errors, write to error stream and continue processing next input in PROCESS{} block of cmdlet

trap

{

Write-Error

continue

}

I provided example of what I want in original post. Can it be done?

Note that you are using old V1 error methods which we pretty much don't use anymore.  Use Try/Catch.  Do not set a global "Stop" do as Fasuto recommends and use "Stop" on the CmdLet.

function test{

    Try{
        Get-Service helloWoorld -ea Stop
    }

    Catch{ 'I caught the error' }
    'script continues.'
}

Test
Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 1:47pm

I need exactly what I have which is trap statement with Write-Error inside it. This does not seem to work.

Trap seems to be supported and alive method. Was it deprecated or advised to be stopped using?

April 23rd, 2015 1:50pm

artisticcheese, as you know there's more than one way of doing things. So show us your code and we'll have a look. You're not allowing us to help you as it is.
Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 1:57pm

artisticcheese, as you know there's more than one way of doing things. So show us your code and we'll have a look. You're not allowing us to help you as it is.
My code is in the first message. Very first message which started this conversation. 
April 23rd, 2015 1:58pm

I need exactly what I have which is trap statement with Write-Error inside it. This does not seem to work.

Trap seems to be supported and alive method. Was it deprecated or advised to be stopped using?

It has been deprecated sins PowerShell 2 or WMF 2.0. WMF 2 will be obsolete as of June when WS2003 goes out.  Remember that MS only supports 2 previous versions of anything and we are at PowerShell 4 or WMF 4.0 with WMF 5.0 being redied.

Trap does not work well with newer PS capabilities.  Try/Catch is a system level exception handler that is used by every subsystem and in almost all computer systems including Unix and MAC.  I know of no one using Trap for years now.

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 2:25pm


My code is in the first message. Very first message which started this conversation. 

So your code is to use Write-Errors, but you don't want Write-Error to display an error, and you want it to be caught as if it was a terminating error?

Because that's what you're showing right now. And that's mental.

April 23rd, 2015 2:44pm


My code is in the first message. Very first message which started this conversation. 

So your code is to use Write-Errors, but you don't want Write-Error to display an error, and you want it to be caught as if it was a terminating error?

Because that's what you're showing right now. And that's mental.

I want my code to write-error on both terminating and non-terminating errors inside PROCESS{} block without wrapping entire block or parts of it inside try{}catch{}

What is so mental about it?

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 2:59pm

I need exactly what I have which is trap statement with Write-Error inside it. This does not seem to work.

Trap seems to be supported and alive method. Was it deprecated or advised to be stopped using?

It has been deprecated sins PowerShell 2 or WMF 2.0. WMF 2 will be obsolete as of June when WS2003 goes out.  Remember that MS only supports 2 previous versions of anything and we are at PowerShell 4 or WMF 4.0 with WMF 5.0 being redied.

Trap does not work well with newer PS capabilities.  Try/Catch is a system level exception handler that is used by every subsystem and in almost all computer systems including Unix and MAC.  I know of no one using Trap for year

April 23rd, 2015 3:01pm

I want my code to write-error on both terminating and non-terminating errors inside PROCESS{} block without wrapping entire block or parts of it inside try{}catch{}

What is so mental about it?

If you want to do it without try..catch, everything about it is mental.

I understand wanting to catch errors thrown by some other function you call and raising your own errors instead, but that's now how you do it.

Quality of code is not measured by how few lines one writes (nor by how many).

If you want to not use try...catch you'll struggle...

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 3:08pm

I want my code to write-error on both terminating and non-terminating errors inside PROCESS{} block without wrapping entire block or parts of it inside try{}catch{}

What is so mental about it?

If you want to do it without try..catch, everything about it is mental.

I understand wanting to catch errors thrown by some other function you call and raising your own errors instead, but that's now how you do it.

Quality of code is not measured by how few lines one writes (nor by how many).

If you want to not use try...catch you'll struggle...

I want to use trap{} statement as it's intended and documented by Microsoft to catch every error inside PROCESS{} block do something with it and essentially rethrow it without terminating process. It's not possible to do they way I want it, right?
April 23rd, 2015 3:12pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            Write-Host "We had an error:  $_" -fore red -back white
            #throw $_
            #Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}
1,2,3,0,4,5,0,6,7,8|Test

By the way.. Write-Error is not the best way to re-throw an error. 

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 3:25pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            Write-Host "We had an error:  $_" -fore red -back white
            #throw $_
            #Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}
1,2,3,0,4,5,0,6,7,8|Test

By the way.. Write-Error is not the best way to re-throw an error. 

April 23rd, 2015 3:38pm

Write-Error will generate a non-terminating error.

Setting the ErrorActionPreference to stop forces PS to stop all execution (including of the whole script, not just inside your function) as soon as either a terminating or non-terminating error is caught.

You shouldn't really set the global ErrorAction to stop. Instead, run cmdlets (and advanced functions) with ErrorAction -Stop if you need to catch non-terminating errors.

For example:

# Uncaught non-terminating error 1/0

#Since error is non-terminating, rest of script still runs

try { # -ErrorAction stop forces errors from Get-Process to be treated as terminating, which allows me to catch it. Get-Process htoajhth -ErrorAction Stop } catch { 'I caught the error' } 'I still get executed'.




  • Edited by cogumel0 Thursday, April 23, 2015 4:19 PM
Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 4:17pm

Once there is an error it is in the error stream.  You cannot delete it without stopping and clearing the error stack.  Just type $error at a prompt after the function or scrip texts and all errors will be there.

You need to spend more time studying how PowerShell is designed.  You are trying to force every thing to work your way when any good tech knows that we have to adapt to the system. Just because you learned to use Trap first does not mean it is the right tool to use always.

Try finding one of the many good books on PowerShell and see what they say.  None of  the new ones more than mention Trap in passing.  They all have a chapter and more on Try/Catch.

You are free to try to figure out how to make Trap work the way you want.  None of the rest of us will waste time on it as you have already seen.

help about_try_catch

April 23rd, 2015 4:50pm

Once there is an error it is in the error stream.  You cannot delete it without stopping and clearing the error stack.  Just type $error at a prompt after the function or scrip texts and all errors will be there.

You need to spend more time studying how PowerShell is designed.  You are trying to force every thing to work your way when any good tech knows that we have to adapt to the system. Just because you learned to use Trap first does not mean it is the right tool to use always.

Try finding one of the many good books on PowerShell and see what they say.  None of  the new ones more than mention Trap in passing.  They all have a chapter and more on Try/Catch.

You are free to try to figure out how to make Trap work the way you want.  None of the rest of us will waste time on it as you have already seen.

help about_try_catch

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 4:53pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Imagine I want to send email in error handler and still output Error stream for consuming application to listen to.

This passes the error back up the chain:

Throw $_

Write-Error is designed to be used to create custom errors so if you decide that a file names is not in the right format you can generate a custom error to be used by the rest of the system.  Trap is not for generating custom errors. In a "Trap" we  would just re-throw the error if we were not going to handle it.

April 23rd, 2015 4:53pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Imagine I want to send email in error handler and still output Error stream for consuming application to listen to.

This passes the error back up the chain:

Throw $_

Write-Error is designed to be used to create custom errors so if you decide that a file names is not in the right format you can generate a custom error to be used by the rest of the system.  Trap is not for generating custom errors. In a "Trap" we  would just re-throw the error if we were not going to handle it.

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 4:56pm

jrv, artisticcheese it's like you both are talking a completely different language...

I get where you're coming from artisticcheese. You basically want to catch an error and re-throw it as a non-terminating error so that the error is thrown, but the script continues.

For that, you'd use Write-Error $_ instead of throw $_, the rest of the script is fine.

@jrv, there's a lot of situations where re-throwing an error, regardless of whether it was originally terminating or not is very useful.

An example: imagine I write a module that I share with the community and inside one of my functions I have:

Get-Service $name


Where $name happens to have the value of a non-existing service. If I don't catch it, it will look like this:

Get-Service : Cannot find any service with service name 'fhij'.
At line:121 char:1
+ Get-Service $name
+ ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (fhij:String) [Get-Service], ServiceCommandException
    + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand

However, the person implementing my module will look at their code and go... I don't have a line 121... my script has 2 lines... I'm not calling Get-Service... Why am I getting this error?

In these situations, there's a lot of good to come from catching, re-writing and re-throwing the error. And in some cases you want to catch a non-terminating error and re-throw it as terminating, because you have a better idea of what you're doing with the code.

A very good example of this is a DSC resource. Do you really want it to continue after it's encountered issues?

April 23rd, 2015 5:46pm

@artisticcheese, so the script should look like this:

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
		#Get-Service "DD"
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            #Write-Host "We had an error:  $_" -fore red -back white
            #throw $_
            Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}
1,2,3,0,4,5,0,6,7,8|Test

And, if you want to ensure that it terminates the script on the very first error, you just call it as such:

1,2,3,0,4,5,0,6,7,8|Test -ErrorAction Stop
The beauty of doing this is that the user now has control of how they want your function to behave, and you never have to modify the function itself. You're not hard coding that this is a terminating error or that it is not. They decide it.


Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 5:49pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Imagine I want to send email in error handler and still output Error stream for consuming application to listen to.

This passes the error back up the chain:

Throw $_

Write-Error is designed to be used to create custom errors so if you decide that a file names is not in the right format you can generate a custom error to be used by the rest of the system.  Trap is not for generating custom errors. In a "Trap" we  would just re-throw the error if we were not going to handle it.

April 23rd, 2015 7:10pm

You can try to do this and it will work easily.  Arguing will only get you stuck for a bit:

Try this to see how it works.

Imagine I want to send email in error handler and still output Error stream for consuming application to listen to.

This passes the error back up the chain:

Throw $_

Write-Error is designed to be used to create custom errors so if you decide that a file names is not in the right format you can generate a custom error to be used by the rest of the system.  Trap is not for generating custom errors. In a "Trap" we  would just re-throw the error if we were not going to handle it.

Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 7:16pm

And, if you want to ensure that it terminates the script on the very first error, you just call it as such:

1,2,3,0,4,5,0,6,7,8|Test -ErrorAction Stop
The beauty of doing this is that the user now has control of how they want your function to behave, and you never have to modify the function itself. You're not hard coding that this is a terminating error or that it is not. They decide it.


Shhh! Don't give away all of the secrets of PosH
April 23rd, 2015 7:18pm

artisticcheese, read my last post
Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 7:19pm

Now lets try it with a non-terminating error and use the common parameters.

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
            Get-Service noservice
        }
        Catch{
            Write-Host "We had an error:  $_" -fore red -back white
            #throw $_
            #Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}
1,2,3,0,4,5,0,6,7,8|Test
1,2,3,0,4,5,0,6,7,8|Test -ea stop





April 23rd, 2015 7:33pm

@artisticcheese, so the script should look like this:

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
		#Get-Service "DD"
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            #Write-Host "We had an error:  $_" -fore red -back white
            #throw $_
            Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}
1,2,3,0,4,5,0,6,7,8|Test

And, if you want to ensure that it terminates the script on the very first error, you just call it as such:

1,2,3,0,4,5,0,6,7,8|Test -ErrorAction Stop
The beauty of doing this is that the user now has control of how they want your function to behave, and you never have to modify the function itself. You're not hard coding that this is a terminating error or that it is not. They decide it.


Free Windows Admin Tool Kit Click here and download it now
April 23rd, 2015 9:42pm

Yeah, but now there's no point to the try..catch in Test. It gets ignored if there is no -ErrorAction that catches non-terminating errors, and if the user wants to catch non-terminating errors (by using -ErrorActions Stop) he should implement his own try..catch outside it, as he's now asking for all errors to be terminating, so he needs to grab them.

Also, if I were to put the function Test on a module of its own, open PowerShell and type:

PS C:> 1,2,3,0,4,5,0,6,7,8|Test

I would be presented with...

Get-Service : Cannot find any service with service name 'noservice'.
At line:8 char:13
+             Get-Service noservice
+             ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (noservice:String) [Get-Service], ServiceCommandException
    + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand

But I have no idea what the function Test does, I downloaded that as a module off the internet. I wrote a single line in a brand new PowerShell session, there is no line 8, there is no Get-Service... So you should catch this error and re-throw it, so it's 'branded' as coming from Test and the line that called Test.

April 24th, 2015 8:02am

I think the best way to handle this, and from what I understand what the OP is after... is this:

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}

# This will throw non-terminating errors, allowing the pipeline to continue...
1,2,3,0,4,5,0,6,7,8|Test

# This will throw terminating errors, meaning the whole script will exit at the first error caught.
1,2,3,0,4,5,0,6,7,8|Test -ErrorAction -Stop

# Notice that the function stayed the same, but the user has control over whether to 'force' the errors to be considered terminating or not, without changing the function at all.

Free Windows Admin Tool Kit Click here and download it now
April 24th, 2015 8:05am

Now, if all this would work with trap{} statement instead then it would be perfect. I find code cleaner and easier to read with trap statement in a block instead of try{}catch{} statements all over the place or wrapping entire code in try{}catch{} which is essentially the same as trap{}
April 24th, 2015 8:14am

Now, if all this would work with trap{} statement instead then it would be perfect. I find code cleaner and easier to read with trap statement in a block instead of try{}catch{} statements all over the place or wrapping entire code in try{}catch{} which is essentially the same as trap{}

Actually as I posted above, Trap and try/catch are fundamentally different.  There is much more to SEH than what Trap is able to handle. Read the help and look up SEH to see.  Trap will eventually be removed from PowerShell so better start learning how to use modern programming technology.

Here is a place to start: https://www.microsoft.com/msj/0197/exception/exception.aspx

If you seaqrch you will see that exceptin handling is used in nearly all languages: https://www.google.com/#newwindow=1&q=try%2Fcatch

Free Windows Admin Tool Kit Click here and download it now
April 24th, 2015 10:35am

I think the best way to handle this, and from what I understand what the OP is after... is this:

Function Test{
    Param(
    [Parameter(ValueFromPipeline)]$x
    )
    Begin{Write-Host 'Begin called' -fore blue}
    Process{
        Try{
            Write-Host "`tAnswer: $(10/$x)"  -fore green
        }
        Catch{
            Write-Error $_
        }
    }
    End{Write-Host 'End Called' -fore blue}
}

# This will throw non-terminating errors, allowing the pipeline to continue...
1,2,3,0,4,5,0,6,7,8|Test

# This will throw terminating errors, meaning the whole script will exit at the first error caught.
1,2,3,0,4,5,0,6,7,8|Test -ErrorAction -Stop

# Notice that the function stayed the same, but the user has control over whether to 'force' the errors to be considered terminating or not, without changing the function at all.

April 24th, 2015 12:00pm

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics