|There's more than one way to do things|
Doesn't sound like it to me. If it was, you'd just call the routine that produces an error message, and let that routine use its Big Switch Statement to decide how exactly to do that. (If the routine that prints an error message encounters an error while doing so, you'd probably consider that fatal and die.)
I think this part of the confusion might be due to the phrase "the routine that produces an error message". One of the advantages that exceptions give you is that there isn't a error handler, but multiple error handlers.
We don't want our low-level abstraction to have to know about the various ways its being used. We don't want our high-level abstractions to have to know about each other. If we have a single error handler then it has to know about every possible use. Every time we add a new way of handling the error we have to add it to our Big Switch Statement. Having to have one piece of code that knows about every possible usage is producing needlessly tight coupling between modules.
With an exception handling style error conditions and error handlers are separated. There is no "Big Switch Statement". Each error handler is local to the module that needs to handle an error condition in a particular way.
You can happily add new modules that handle the error in a different way without having to alter any other piece of code.
I'm starting to think maybe we're doing the same thing in opposite ways. You're reducing complexity by moving all knowledge of what's going on into the caller, and I'm reducing complexity by leaving the caller with knowledge only of what it wants to do with the data and moving all knowlege of where the data comes from into the subroutine. It may be a different paradigm.
I would characterise the difference as where you choose to handle the error.
Passing the high-level context down to the error handler is can be tricky. You've got globals, a Big Switch Statement, or you have to pass the calling context from the highest level down to the lowest level in some way (adding a special error handling object to every call for example). All of which (in my opinion) make the code harder to maintain - especially if you have a system with many layers and many possible error conditions and handlers.
There are, of course, some situations where you have to handle the error in the context it occurs in to be able to do anything useful. Some languages provide direct support for doing this sort of error handling cleanly (e.g. handler-bind in Common Lisp), but they provide exception handling too!
That said, in my experience, having to handle the error in the calling context of the routine that caused the error is rarely necessary.
Passing the low-level context up to an error handler is simple - it goes into the exception object when the error occurs.
In some ways, your approach reminds me very much of my brief experiments with the event-oriented paradigm when I took courses in two "fifth-generation" languages in college. As you probably know, event-orientation turns everything around by making user input the caller and the program's logic the callee. I have a bad taste in my mouth for this approach, possibly because the only languages I've used that do things that way are VB and Lingo, both of which I loathe, especially Lingo. Though now that I think of it, it might be possible to do something like it in Perl, sort of, and that might not be so bad. ponders this
Don't throw away event-handling because of VB & Lingo :-) It's a very useful coding style, and pretty much essential for many kinds of problem. Take a look at POE for one perl approach.