3

I have multiple methods that could fail, let's say :

  1. readFile()
  2. parseFile()
  3. compute()

Each of them could fail (e.g file not found, wrong file type, wrong data), should I put each of them in their own try/catch block or should I create one try/catch block then switch the error and log/display text depending on the error?

e.g:

try {
    readfile()
}
catch (err) {
    log.error(err);
    throw 'Could not read file';
}

try {
    parsefile()
}
catch (err) {
    log.error(err);
    throw 'File corrupted or wrong type';
}

try {
    compute()
}
catch (err) {
    log.error(err);
    throw 'Data is wrong';
}

or :

try {
    readfile()
    parsefile()
    compute()
}
catch (err) {
    switch(err) {
        case 'NOENT': throw...
        case 'CORRUPTED': throw...
    }
}

3 Answers 3

5

I am a believer that the best reason to catch an exception is to be able to log and then recover so as to continue running the program, for example, informing the user of the failure so they can take an alternate action.

What you're discussing here is alternative constructs/patterns for catching that all re-throw.

This implies that you necessarily have another try/catch higher up in the call chain that is doing this recovery.

This higher in the call-stack try/catch can do the logging, which makes the local try/catch (close to the point of throw, where corrective action cannot be taken) seem redundant to me, and I would seek to eliminate these seemingly unnecessary constructs.

try {
    parsefile()
}
catch (err) {
    log.error(err);
    throw 'File corrupted or wrong type';
}

For example, the above try/catch could be removed by (1) relying on the outer try/catch to log the error and recover the program, and (2) throwing the proper exception in parsefile() in the first place.

(It could also be the case that the the parsefile() is already throwing a more useful and specific exception that is then being obscured by re-throwing a different more general exception.  Let's recall that exceptions in most languages can participate in hierarchy to simplify some issues here.)

(Of course, there are times when this is impractical, but the question is posed rather generally, so this is a general answer)

1
  • There is a parent component catching all the errors and displaying it to the final user (in this case it's an API) - thus the throw in my catch. The logging is mostly for debugging in case of errors, if I remove the try catch here, it just breaks the program and stops instead of keep going (asynchronous code in JS) since the code is in a separate module.
    – toto
    Nov 26, 2018 at 8:45
3

In most situations, I would say "neither". Instead, you should probably go with one try followed by multiple catch parts.

try {
    readFile();
    parseFile();
    compute();
} catch (ReadFileException err) {
    log.error(err);
    throw new SomeException("Cannot read file", err);
} catch (ParseFileException err) {
    log.error(err);
    throw new SomeException("Cannot parse file", err);
} catch (ComputeException err) {
    log.error(err);
    throw new SomeException("Cannot compute results of file", err);
}

This keeps your "happy flow" code bundled together, while making it easy to handle different cases. Your second option has these same advantages, but seems to match on some error message, which is rather brittle. Using different exception types gives your IDE the option to help you.

One note: If there is actually a lot of stuff going on between readFile(), parseFile() and compute(), I would go with 3 try..catch blocks instead - what "a lot of stuff" means in that case is very much a matter of personal taste, I think, and something you need to get a feel for.

8
  • The second option is also viable if OP doesn't really care or need to know the specific error that occurred. Nov 23, 2018 at 16:14
  • One question arises: why do you think a SomeException is more useful to your caller than e.g. a ReadFileException? Nov 23, 2018 at 19:26
  • For Java and similar, this makes perfect sense. However this would not apply to, say, Javascript at all.
    – cbojar
    Nov 25, 2018 at 17:22
  • @RalfKleberhoff Because the fact that there are files and parsing and computing isn't something the caller should know about, the caller just wants to know the end result. To me letting such exceptions bubble up feels like making things more complicated for the caller, without adding actual value. Nov 26, 2018 at 7:44
  • 1
    @JoryGeerts "The caller wants to know the end result": yes, exactly, and to me that means, he won't care about the type of exception, just the fact that he got some exception, be it a SomeException, a ReadFileException, an IOException, a NullPointerException or whatever - they all mean the same to him. That's why I'd stay with the original one (unless forced to change because of some method signature e.g. in Java). Nov 26, 2018 at 21:18
1

I fully agree with Erik's point of view, and I'd like to add some more reasoning to support our recommendation:

    readFile();
    parseFile();
    compute();

without any try/catch.

What is an exception?

  • It signals to your caller that your method did not succeed (fulfill its contract) for whatever reason.
  • It contains a description of that failure, typically by using a specific exception class, containing a message and saving the stack trace where the exception happened.
  • It abruptly aborts the current control flow in the current and all parent method executions, up to a point where you placed a matching try/catch.

This concept of exception has been thoughtfully designed to give you a well-behaving error handling nearly for free.

In your sample code, you want to catch exceptions and re-throw different ones. Why? What do you think your caller will do based on the exception? And how will your translated exceptions make that job easier?

I have to disappoint you: typically your caller isn't interested at all in the reason of the failure. If your caller gets an exception, he'll just abort himself and tell his caller about that fact (typically by just letting the uncaught exception ripple through). So, replacing the exception with a different one is a waste of time in 99% of cases. I've rarely ever seen code where a method took different branches depending on the type of exception it got.

Somewhere up in the call stack, there's a place where some developer decided to put a try/catch, because he thinks he can continue here. But he'll typically not care about the type of exception. Maybe, he'll retry the call, or just show a message to the user and wait for his next action. And all this works with any type of exception, so there's again no reason to translate exceptions.

One thing you should care about is logging. Make sure you log each exception once, and only once. The sysadmins running your software will hate you if you clutter the logfiles with endless repetitions of the same problem, especially if you re-word it at every catch/re-throw level. So, don't log in places where you re-throw the exception. Error logging belongs in places where exceptions are caught for good.

Make sure every exception carries a useful message. Typically all the exceptions coming from mature libraries will typically have a useful message, but there might be cases where you want to add information (e.g. about context like an object ID), and that's a valid reason for a catch/re-throw. Don't log here, but create and throw a new exception containing the extra info and referring to the original exception, if your language supports that (e.g. Java does). So the log eintry has all the original exception information, typically including the stack trace, being the most important debugging aid available in a production setting.

To sum it up with The Golden Rule of Exception Handling:

  • You Shouldn't Catch an Exception Here.
  • You Really Shouldn't Catch an Exception Here.
  • If Despite of The Rule You Catch an Exception Here, Be Sure You Know a Good Reason Why!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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