The fundamental problem, IMHO, with error-handling is the call/return nature of most of our programming languages. That means you have to return something from you function/procedure. If you have nothing to return (error) or nothing to return yet (async), you have a problem, particularly with intermediate functions that don't really know what happened below them and don't really have enough of the context from their callers to handle the error.
Exceptions allow you to skip the these intermediate layers. ROP makes you handle all those layers, but gives you some tools to help make sure you did it correctly.
Filters, on the other hand, do not return results. Instead they actively pass their results on to the next filter in line. That means that in the case of an error, they can do what functions/procedures cannot: simply not produce a result at all. The next filter will be none-the-wiser.
You can then have the filter have an out-of-band mechanism for actually dealing with the error, analogous to Unix stderr.