Here's a piece of code for you to wrap your head around:
1..10 | ForEach-Object { trap { Write-Host "Phew: $_" continue } $_ } | ForEach-Object { if ($_ -gt 4) { Throw "Too big!" } else { $_ } }
Ten numbers are fed into the pipeline. They reach the downstream cmdlet ForEach-Object which basically only implements an error handler (trap) and then passes the number on to the next downstream cmdlet which happens to be another ForEach-Object.
The second ForEach-Object checks the number, and if it is greater than 4, it throws an error.
Pipelines usually work strictly downstream, so if something happens in a downstream cmdlet, upstream cmdlets won't notice. Errors (exceptions), however, travel the opposite direction, so when you run the code, this is the result:
1 2 3 4 Phew: Too big! Phew: Too big! Phew: Too big! (...)
Although the second ForEach-Object encountered some special situation, it is the first (upstream) cmdlet that responds to it.
This basically enables you to establish an inner-pipeline-communication-system where a downstream cmdlet can tell an upstream cmdlet: "I've had enough"! Coincidentally, this is how PowerShell 3.0 implemented Select-Object with its parameter -First. Once the first x elements are received, upstream cmdlets stop.