180

There are many well-known best practices about exception handling in isolation. I know the "do's and don'ts" well enough, but things get complicated when it comes to best practices or patterns in larger environments. "Throw early, catch late" - I've heard many times and it still confuses me.

Why should I throw early and catch late, if at a low-level-layer a null pointer exception is thrown? Why should I catch it at a higher layer? It doesn't make sense for me to catch a low-level exception at a higher level, such as a business layer. It seems to violate the concerns of each layer.

Imagine the following situation:

I have a service which calculates a figure. To calculate the figure the service accesses a repository to get raw data and some other services to prepare calculation. If something went wrong at the data retrieval layer, why should I throw a DataRetrievalException to higher level? In contrast I would prefer to wrap the exception into a meaningful exception, for example a CalculationServiceException.

Why throw early, why catch late?

12
  • 125
    The idea behind "catch late" is, rather than catching as late as possible, to catch as early as is useful. If you have a file parser, for example, there's no point handling a file not found. What are you meant to do with that, what is your recovery path? There isn't one, so don't catch. Go to your lexer, what do you do there, how do you recover from this such that your program can continue? It can't, let the exception pass. How can your scanner handle this? It can't, let it pass. How can the calling code handle this? It can either try another filepath or alert the user, so catch.
    – Phoshi
    Mar 3, 2014 at 12:34
  • 17
    There are very very few cases where a NullPointerException (I assume that's what NPE means) should ever be caught; if possible, it should be avoided in the first place. If you're having NullPointerExceptions, then you have some broken code that needs to be fixed. It's most likely an easy fix too.
    – Phil
    Mar 3, 2014 at 14:48
  • 6
    Please, guys, before suggesting to close this question as a duplicate, check that the answer to the other question does not answer this question very well.
    – Doc Brown
    Mar 3, 2014 at 16:18
  • 1
    (Citebot) today.java.net/article/2003/11/20/… If this is not the origin of the quote, please provide a reference to the source which you think is the most-likely original quote.
    – rwong
    Jun 25, 2014 at 11:26
  • 2
    Just a reminder for anyone reaching to this question and doing Android development. On Android, exceptions must be caught and handled locally - in the same function where it is first caught. This is because exceptions don't propagate across message handlers - your application will be killed if it happens. So, you should not cite this advice when doing Android development.
    – rwong
    Oct 8, 2014 at 21:21

8 Answers 8

135

In my experience, its best to throw exceptions at the point where the errors occur. You do this because it's the point where you know the most about why the exception was triggered.

As the exception unwinds back up the layers, catching and rethrowing is a good way to add additional context to the exception. This can mean throwing a different type of exception, but include the original exception when you do this.

Eventually the exception will reach a layer where you are able to make decisions on code flow (e.g a prompt the user for action). This is the point where you should finally handle the exception and continue normal execution.

With practice and experience with your code base it becomes quite easy to judge when to add additional context to errors, and where it's most sensible to actually, finally handle the errors.

Catch → Rethrow

Do this where you can usefully add more information that would save a developer having to work through all the layers to understand the problem.

Catch → Handle

Do this where you can make final decisions on what is an appropriate, but different execution flow through the software.

Catch → Error Return

Whilst there are situations where this is appropriate, catching exceptions and returning an error value to the caller should be considered for refactoring into a Catch → Rethrow implementation.

10
  • 9
    @shylynx Catching an exception and then rethrowing a more meaningful exception is a good thing to do. What you shouldn't be doing is catching an exception too early and then not rethrowing it. The mistake that the saying is warning about is catching the exception too early, and then attempting to handle it at the wrong layer in the code.
    – Simon B
    Mar 3, 2014 at 14:27
  • 1
    I catch->Handle early sometimes, when a method is more usable with a simple boolean return. But exceptions to every rule I guess.
    – Cruncher
    Mar 3, 2014 at 14:40
  • 5
    @shylynx One might ask the question, "Why do you have a point in your code that can throw a NullPointerException? Why not check for null and throw an exception (maybe an IllegalArgumentException) sooner so that the caller knows exactly where the bad null got passed in?" I believe that would be what the "throw early" part of the saying would suggest.
    – jpmc26
    Mar 4, 2014 at 0:20
  • 2
    @jpmc I took the NPE only as an example to emphasize the concerns around the layers and the exceptions. I could replace it with an IllegalArgumentException as well.
    – shylynx
    Mar 4, 2014 at 7:20
  • 1
    @cruncher I think i'd probably disagree with you on this, as forcing all consumers of your method to validate success and handle errors with no information on the actual error cannot possibly be more convenient than being able to assume success and processing errors as exceptions. There are situations where allowing exceptions to flow out of the code is not possible, such as across process boundaries, but these are special instances Mar 4, 2014 at 17:42
62

You want to throw an exception as soon as possible because that makes it easier to find the cause. For example, consider a method that could fail with certain arguments. If you validate the arguments and fail at the very beginning of the method, you immediately know the error is in the calling code. If you wait until the arguments are needed before failing, you have to follow the execution and figure out if the bug is in the calling code (bad argument) or the method has a bug. The earlier you throw the exception, the closer it is to its underlying cause and the easier it is to figure out where things went wrong.

The reason exceptions are handled at higher levels is because the lower levels don't know what's the appropriate course of action to handle the error. In fact, there could be multiple appropriate ways to handle the same error depending on what the calling code is. Take opening a file for example. If you're trying to open a config file and it's not there, ignoring the exception and proceeding with the default configuration can be an appropriate response. If you're opening a private file that's vital to the program's execution and it's somehow missing, your only option is probably to close the program.

Wrapping the exceptions in the right types is a purely orthogonal concern.

2
  • 1
    +1 for clearly explaining why the different levels matter. Excellent example on the file system error. Mar 6, 2015 at 21:36
  • Good answer, I really like that file system example. Can you tell me good resource to catch upon these principles?
    – Suraj Jain
    Dec 25, 2019 at 7:09
30

Others have summarized quite well why to throw early. Let me concentrate on the why to catch late part instead, for which I haven't seen a satisfying explanation for my taste.

SO WHY EXCEPTIONS?

There seems to be quite a confusion around why exceptions exist in the first place. Let me share the big secret here: the reason for exceptions, and exception handling is... ABSTRACTION.

Have you seen code like this:

static int divide(int dividend, int divisor) throws DivideByZeroException {
    if (divisor == 0)
        throw new DivideByZeroException(); // that's a checked exception indeed

    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    try {
        int res = divide(a, b);
        System.out.println(res);
    } catch (DivideByZeroException e) {
        // checked exception... I'm forced to handle it!
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

That's not how exceptions should be used. Code like the above do exist in real life, but they are more of an aberration, and are really the exception (pun). The definition of division for example, even in pure mathematics, is conditional: it is always the "caller code" who has to handle the exceptional case of zero to restrict the input domain. It's ugly. It's always pain for the caller. Still, for such situations the check-then-do pattern is the natural way to go:

static int divide(int dividend, int divisor) {
    // throws unchecked ArithmeticException for 0 divisor
    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt();
    if (b != 0) {
        int res = divide(a, b);
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

Alternatively, you can go full commando on OOP style like this:

static class Division {
    final int dividend;
    final int divisor;

    private Division(int dividend, int divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    public boolean check() {
        return divisor != 0;
    }

    public int eval() {
        return dividend / divisor;
    }

    public static Division with(int dividend, int divisor) {
        return new Division(dividend, divisor);
    }
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    Division d = Division.with(a, b);
    if (d.check()) {
        int res = d.eval();
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

As you see, the caller code has the burden of the pre-check, but does not do any exception handling after. If an ArithmeticException ever comes from calling divide or eval, then it is YOU who has to do the exception handling and fix your code, because you forgot the check(). For the similar reasons catching a NullPointerException is almost always the wrong thing to do.

Now there are some folks who say they want to see the exceptional cases in the method / function signature, i.e. to explicitly extend the output domain. They are the ones who favor checked exceptions. Of course, changing the output domain should force any direct caller code to adapt, and that would indeed be achieved with checked exceptions. But you don't need exceptions for that! That's why you have Nullable<T> generic classes, case classes, algebraic data types, and union types. Some OO people might even prefer returning null for simple error cases like this:

static Integer divide(int dividend, int divisor) {
    if (divisor == 0) return null;
    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    Integer res = divide(a, b);
    if (res != null) {
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

Technically exceptions can be used for the purpose like the above, but here is the point: exceptions do not exist for such usage. Exceptions are pro abstraction. Exception are about indirection. Exceptions allow for extending the "outcome" domain without breaking direct client contracts, and deferring error handling to "somewhere else". If your code throws exceptions which are handled in direct callers of the same code, without any layers of abstraction in between, then you are doing it W. R. O. N. G.

HOW TO CATCH LATE?

So here we are. I have argued my way through to show that using exceptions in the above scenarios is not how exceptions are meant to be used. There exists a genuine use case though, where the abstraction and indirection offered by exception handling is indispensable. Understanding such usage will help understanding the catch late recommendation too.

That use case is: Programming Against Resource Abstractions...

Yeah, business logic should be programmed against abstractions, not concrete implementations. Top level IOC "wiring" code will instantiate the concrete implementations of the resource abstractions, and pass them down to the business logic. Nothing new here. But the concrete implementations of those resource abstractions can potentially be throwing their own implementation specific exceptions, can't they?

So who can handle those implementation specific exceptions? Is it possible to handle any resource specific exceptions at all in the business logic then? Nope, it is not. The business logic is programmed against abstractions, which does exclude the knowledge of those implementation specific exception details.

"Aha!", you might say: "but that's why we can subclass exceptions and create exception hierarchies" (check out Mr. Spring!). Let me tell you, that's a fallacy. Firstly, every reasonable book on OOP says concrete inheritance is bad, yet somehow this core component of JVM, exception handling, is closely tied with concrete inheritance. Ironically, Joshua Bloch could not have written his Effective Java book before he could get the experience with a working JVM, could he? It is more of a "lessons learnt" book for the next generation. Secondly, and more importantly, if you catch a high-level exception then how are you going to HANDLE it? PatientNeedsImmediateAttentionException: do we have to give her a pill or amputate her legs!? How about a switch statement over all the possible subclasses? There goes your polymorphism, there goes the abstraction. You got the point.

So who can handle the resource specific exceptions? It must be the one who knows the concretions! The one who instantiated the resource! The "wiring" code of course! Check this out:

Business logic coded against abstractions... NO CONCRETE RESOURCE ERROR HANDLING!

static interface InputResource {
    String fetchData();
}

static interface OutputResource {
    void writeData(String data);
}

static void doMyBusiness(InputResource in, OutputResource out, int times) {
    for (int i = 0; i < times; i++) {
        System.out.println("fetching data");
        String data = in.fetchData();
        System.out.println("outputting data");
        out.writeData(data);
    }
}

Meanwhile somewhere else the concrete implementations...

static class ConstantInputResource implements InputResource {
    @Override
    public String fetchData() {
        return "Hello World!";
    }
}

static class FailingInputResourceException extends RuntimeException {
    public FailingInputResourceException(String message) {
        super(message);
    }
}

static class FailingInputResource implements InputResource {
    @Override
    public String fetchData() {
        throw new FailingInputResourceException("I am a complete failure!");
    }
}

static class StandardOutputResource implements OutputResource {
    @Override
    public void writeData(String data) {
        System.out.println("DATA: " + data);
    }
}

And finally the wiring code... Who handles concrete resource exceptions? The one who knows about them!

static void start() {
    InputResource in1 = new FailingInputResource();
    InputResource in2 = new ConstantInputResource();
    OutputResource out = new StandardOutputResource();

    try {
        ReusableBusinessLogicClass.doMyBusiness(in1, out, 3);
    }
    catch (FailingInputResourceException e)
    {
        System.out.println(e.getMessage());
        System.out.println("retrying...");
        ReusableBusinessLogicClass.doMyBusiness(in2, out, 3);
    }
}

Now bear with with me. The above code is simplistic. You might say you have an enterprise application / web container with multiple scopes of IOC container managed resources, and you need automatic retries and reinitialization of session or request scope resources, etc. The wiring logic on the lower level scopes might be given abstract factories to create resources, therefore not being aware of the exact implementations. Only higher level scopes would really know what exceptions those lower level resources can throw. Now hold on!

Unfortunately, exceptions only allow indirection over the call stack, and different scopes with their different cardinalities usually run on multiple different threads. No way to communicate through that with exceptions. We need something more powerful here. Answer: async message passing. Catch every exception at the root of the lower level scope. Don't ignore anything, don't let anything slip through. This will close and dispose all resources created on the call stack of the current scope. Then propagate error messages to scopes higher up by using message queues / channels in the exception handling routine, until you reach the level where the concretions are known. That's the guy who knows how to handle it.

SUMMA SUMMARUM

So according to my interpretation catch late means to catch exceptions at the most convenient place WHERE YOU ARE NOT BREAKING ABSTRACTION ANYMORE. Do not catch too early! Catch exceptions at the layer where you create the concrete exception throwing instances of the resource abstractions, the layer which knows the concretions of the abstractions. The "wiring" layer.

HTH. Happy coding!

5
  • You are correct that the code supplying the interface will know more about what can go wrong than the code that uses the interface, but suppose a method uses two resources of the same interface type and failures need to be handled differently? Or if one of those resources internally, as an implementation detail not known to its creator, uses other nested resources of the same type? Having the business layer throw WrappedFirstResourceException or WrappedSecondResourceException and requiring the "wiring" layer to look inside that exception to see the root cause of the problem...
    – supercat
    Jun 25, 2014 at 15:13
  • ...may be icky, but it would seem better than assuming that any FailingInputResource exception will be a result of an operation with in1. Actually, I think in many cases the right approach would be to have the wiring layer pass an exception-handling object, and have the business layer include a catch which then invokes that object's handleException method. That method could rethrow, or supply default data, or put up an "Abort/Retry/Fail" prompt and let the operator decide what to do, etc. depending upon what the application required.
    – supercat
    Jun 25, 2014 at 15:16
  • @supercat I understand what you are saying. I would say a concrete resource implementation is responsible to know the exceptions it can throw. It doesn't have to specify everything (there is so called undefined behavior), but it must ensure there is no ambiguity. Also, unchecked run-time exceptions are to be documented. If it contradicts the documentation, then it's a bug. If the caller code is to be expected to do anything sensible about an exception, the bare minimum is that the resource wraps them in some UnrecoverableInternalException, similar to an HTTP 500 error code. Jul 24, 2014 at 10:02
  • @supercat About your suggestion on configurable error handlers: exactly! In my last example the error handling logic is hard-coded, calling the static doMyBusiness method. This was for the sake of brevity, and it is perfectly possible to make it more dynamic. Such a Handler class would be instantiated with some input/output resources, and have a handle method which receives a class implementing a ReusableBusinessLogicInterface. You could then combine/configure to use different handler, resource, and business logic implementations in the wiring layer somewhere above them. Jul 24, 2014 at 10:22
  • Your code checking whether integer division is allowed is incorrect. Division by zero is not the only problem. MININT / -1 is the other problem.
    – gnasher729
    Dec 30, 2019 at 21:22
11

To answer this question properly, let's take a step back and ask an even more fundamental question.

Why is it that we have exceptions in the first place?

We throw exceptions to let the caller of our method know that we couldn't do what we were asked to do. The type of the exception explains why we couldn't do what we wanted to do.

Let's take a look at some code:

double MethodA()
{
    return PropertyA - PropertyB.NestedProperty;
}

This code can obviously throw a null reference exception if PropertyB is null. There are two things that we could do in this case to "correct" for this situation. We could:

  • Automatically create PropertyB if we don't have it; or
  • Let the exception bubble up to the calling method.

Creating PropertyB here could be very dangerous. What reason does this method have to create PropertyB? Surely this would violate the single responsibility principle. In all likelihood, if PropertyB does not exist here, it indicates that something has gone wrong. The method is being called on a partially constructed object or PropertyB was set to null incorrectly. By creating PropertyB here, we could be hiding a much bigger bug which could bite us later on, such as a bug which causes data corruption.

If, instead, we let the null reference bubble up, we are letting the developer who called this method know, as soon as we can, that something went wrong. A vital pre-condition of calling this method has been missed.

So in effect, we are throwing early because it separates our concerns much better. As soon as a fault has occurred, we are letting the upstream developers know about it.

Why we "catch late" is a different story. We don't really want to catch late, we really want to catch as early as we know how to handle the issue properly. Some of the time this will be fifteen layers of abstraction later and some of the time this will be at the point of creation.

The point is that we want to catch the exception at the layer of abstraction that allows us to handle the exception at the point where we have all the information we need to handle the exception properly.

5
  • I think you are using upstream developers in the wrong sense. Also, you said it violates single responsibility principle, but in reality many on demand initialization and value caching is implemented that way (with proper concurrency controls in place of course) Mar 4, 2014 at 11:36
  • In your given example, what about checking for null before the subtraction operation, like this if(PropertyB == null) return 0; Jun 14, 2018 at 20:45
  • 1
    Can you also elaborate on your last paragraph, specifically what do you mean by 'layer of abstraction'. Jun 14, 2018 at 20:48
  • If we are doing some IO work, the layer of abstraction to catch an IO Exception would be where we're doing the work. At that point we have all the information we need to decide if we want to retry or throw up a message box to the user or create an object using a set of defaults.
    – Stephen
    Jun 26, 2018 at 0:17
  • 1
    "In your given example, what about checking for null before the subtraction operation, like this if(PropertyB == null) return 0;" Yuck. That would be telling the calling method that I have valid things to subtract to and from. Of course this is contextual but it would be bad practice to do my error checking here in most cases.
    – Stephen
    Jun 26, 2018 at 0:21
6

Throw as soon as you see something worth throwing for to avoid putting objects in an invalid state. Meaning that if a null pointer was passed you check for it early and throw a NPE before it has a chance to trickle down to the low level.

Catch as soon as you know what to do to fix the error (this is generally not where you throw otherwise you could just use a if-else), if an invalid parameter was passed then the layer that provided the parameter should deal with the consequences.

4
  • 1
    You wrote: Throw soon,... Catch soon...! Why? That's a completely contrary approach in contrast to "throw early, catch late."
    – shylynx
    Mar 3, 2014 at 13:46
  • 1
    @shylynx I don't know where "throw early, catch late" comes from, but its value is questionable. What, exactly, does catch "late" mean? Where it makes sense to catch an exception (if at all) depends on the problem. The only thing that's clear is that you want to detect problems (and throw) as early as you can.
    – Doval
    Mar 3, 2014 at 13:56
  • 2
    I assume "catch late" is meant to contrast the practice of catching before you can know what to do to fix the error -- e.g. sometimes you see functions that catch everything just so they can print an error message and then rethrow the exception.
    – user122173
    Mar 4, 2014 at 19:10
  • @Hurkyl: A problem with "catch late" is that if an exception bubbles up through layers that know nothing about it, it can be difficult for code that might be in a position to do something about the situation to know that things really are as expected. As a simple example, suppose if a parser for a user document file needs to load a CODEC from disk and a disk error occurs while reading that, code which calls the parser may act inappropriately if it thinks there was a disk error while reading the user document.
    – supercat
    Jun 25, 2014 at 15:06
4

A valid business rule is 'if the lower level software fails to calculate a value, then ...'

This can only be expressed at the higher level, otherwise the lower level software is trying to change its behavior based on its own correctness, which is only going to end in a knot.

2

First of all, exceptions are for exceptional situations. In your example, no figure can be calculated if the raw data is not present because it could not be loaded.

From my experience, it's good practice to abstract exceptions while walking up the stack. Typically the points where you want to do this is whenever an exception crosses the boundary between two layers.

If there's an error with gathering your raw data in the data layer, throw an exception to notify whoever requested the data. Do not attempt to work around this issue down here. The complexity of the handling code can be very high. Also the data layer is only responsible for requesting data, not for handling errors that occure while doing this. This is what meant by "throw early".

In your example the catching layer is the service layer. The service itself is a new layer, sitting over the data access layer. So you want to catch the exception there. Perhaps your service has some failover infrastructure and tries to request the data from another repository. If this also fails, wrap the exception inside something the caller of the service understands (if it's an web service this might be an SOAP fault). Set the original exception as inner exception so that the later layers can log exactly what went wrong.

The service fault may be catched by the layer calling the service (for example the UI). And this is what meant by "catch late". If you are not able to handle the exception in an lower layer, rethrow it. If the top most layer can't handle the exception, handle it! This might include logging or presenting it.

The reason why you should rethrow exceptions (like described above by wrapping them in more general exeptions) is, that the user is very likely not able to understand that there was an error because, for example, a pointer pointed to invalid memory. And he doesn't care. He only care's that the figure could not be calculated by the service and this is the information that should be displayed to him.

Going futher you can (in an ideal world) completely leave out try/catch code from the UI. Instead use an global exception handler that is able to understand the exceptions that are possibly thrown by the lower layers, writes them into some log and wraps them into error objects that contain meaningful (and possibly localized) information of the error. Those objects can easily be presented to the user in any form you wish (message boxes, notifications, message toasts, and so on).

1

Throwing exceptions early in general is a good practice because you don't want broken contracts to flow through the code further than necessary. For example, if you expect a certain function parameter to be a positive integer then you should enforce that constraint at the point of the function call instead of waiting until that variable is used somewhere else in the code stack.

Catching late I can't really comment on because I have my own rules and it changes from project to project. The one thing I try to do though is separate the exceptions into two groups. One is for internal use only and the other is for external use only. Internal exception are caught and handled by my own code and external exception are meant to be handled by whatever code is calling me. This is basically a form of catching things later but not quite because it offers me the flexibility to deviate from the rule when necessary in the internal code.

Not the answer you're looking for? Browse other questions tagged or ask your own question.