Drawbacks of a master error handler?

  softwareengineering

I’m thinking that I could simplify my life by making an exception handling class that I can just ship all exceptions to and it will handle appropriately.

Ideally:

def dostuff():
  try:
    dothis()
  except Exception e:
    Handler.handle(e)

To avoid

def dostuff():
  try:
    dothis()
  except ValueError e:
    #handle ValueError
  except IndexError e:
    #handle IndexError

The Handler class would have all the logic to handle exception appropriately and you could pass some arguments to specify how to handle the error, but there would be reasonable defaults to make thing easy.

This seems like either a great idea or a terrible one. Its possible that I will find it lacks the usefulness I’m looking for. But I feel like there may be some serious drawbacks that I’m not seeing. I don’t think I’ve seen this approach often. Is there a reason?

I’m working in Python right now, but I think its not really a language specific question.

2

My conjecture is that if you feel a need for such a helper, you’re probably catching too many exceptions in the first place. What useful action can you take on an exception? You either know the cause of the error from the context (that is, you expect the exception) and can provide a reasonable fall-back strategy, then your generic handler won’t help. Or you don’t know what to do with the exception, then you should simply let it bubble up until somebody eventually will be able to act upon it in a reasonable way. As a last resort, you might have a “catch all” handler in your main function that says “sorry”, logs the exception and terminates the program. For many unexpected errors, this is the appropriate reaction.

Python is a little different from most other languages I know in that it encourages you to use exceptions much more as a form of ordinary control flow but I don’t see how that would change the overall picture. Just as you don’t have a generic helper function to handle return values, you probably don’t want a generic exception handler.

Let me illustrate this with some example.

This is a reasonable use of catching an exception that can’t really be automated away.

def get_foo_factor(fallback=10):
    try:
        return int(os.getenv('FOO_FACTOR'))
    except TypeError | ValueError:
        # Environment variable not set or not an integer.
        return fallback

This one is completely ridiculous.

def append_to_file(filename, message):
    try:
        with open(filename, 'a') as ostr:
            print(message, file=ostr)
    except TypeError:
        print("Not a string?", file=sys.stderr)
    except AttributeError:
        print("This should never happen!", file=sys.stderr)

If your code has a lot of such except cascades, the best refactoring would probably be to simply delete them. For those few that remain, you’d probably find that your automated handler won’t be of much use. The language already has a specialized syntax for making exception handling as convenient as possible, after all.

The only use-case for a handler I can see is if you’re interfacing with a library that throws various exceptions and you want to translate them into your own scheme. In this case, something like the following might be appropriate.

def translate_libfoo_exception(exception):
    try:
        raise exception
    except FooErrorOne as e:
        return MyError(e)
    except FooErrorTwo as e:
        return MyOtherError(e)
    except FooErrorThree as e:
        return MyOtherError(e)
    except FooError as e:
        return MyGenericError(e)
    except Exception as e:
        return e

It could then be used like this.

try:
    foo.bar()
except FooErrorThree:
    # Needs special handling
    pass
except Exception as e:
    # Cannot be handled, translate and re-raise
    raise translate_libfoo_exception(e)

What you lose is context. You’re passing the exception object e, but Handle.handle has no idea whether (say) IndexError happened at an input sanitiser, in which case it might trigger some kind of defensive mechanism; or whether it happened deep in an algorithm after sanitisation, in which case it could flag a problem to the developer. Because it doesn’t have the context in which the exception was raised, Handle.handle wouldn’t know which action to take.

4

A master error handler is akin to a god class, an anti pattern in modular design.

On the other hand, there is a design pattern called Error Handler which allows you to centralize control of errors that are the same.

LEAVE A COMMENT