64

I read a great post about this topic on Stack Overflow: Is it really that bad to catch a general exception?

However, I asked this question on our exchange because I am interested in a more specific discussion of how this topic fits in with a Multitenant Architecture. This discussion is very context specific, and I think it merits a separate discussion specific to what makes sense on the Salesforce platform.


I have a strong preference against catching a generic Exception. One of my former colleagues derisively called this practice a Pokemon Catch, as in "gotta catch em all!"

try
{
    // logic which may throw
}
catch (Exception pokemon)
{
    // handling logic
}

My main problem with catching the generic Exception is that it demonstrates that you do not know the expected behavior or failure paths of your own code. I believe this information is, in general, knowable.

I know that swallowing an Exception silently in the catch block is a much bigger problem, and that's not what I'm asking about here. I'm also not asking about the tendency to find these blocks wrapping an excessive length of code (sometimes an entire method).

Is it ever acceptable (or even preferable) to catch a generic Exception in Apex? Are there repercussions to watch out for?

  • 11
    It's probably worth mentioning that in Apex, you can't actually catch all of the exceptions; System.LimitException being a notable example. – martin Aug 16 '16 at 5:33
  • 2
    Pokemon exceptions are for recovering from specific known issues. Particularly at higher levels in the code, you should only catch it if you can do something about it, which means specific exceptions. – Jon Story Aug 16 '16 at 13:18
  • 9
    @AdrianLarson UnexpectedException and AssertException are also uncatchable. – Alan Morey Aug 16 '16 at 18:26
  • 5
    As I explained in my post, I want an explicit discussion of how this topic relates to Salesforce. It is a different question in Java, and different still in Python, C++, etc. So yes, it is Salesforce specific. – Adrian Larson Aug 16 '16 at 18:46
  • 3
    This question was reopend based on community votes and a topic in our meta. – Samuel De Rycke Aug 19 '16 at 9:16
56

Pros

One of the primary advantages of the Pokemon Catch is that you reduce the complexity of code in many cases. For example, if you're concerned about a NullPointerException, a CalloutException, a QueryException, and a DmlException, you have two choices:

try {
  ...
} catch(NullPointerException npe) {
  ...
} catch(CalloutException ce) {
  ...
} catch(QueryException qe) {
  ...
} catch(DmlException de) {
  ...
}

... or ...

try {
  ...
} catch(Exception e) {
  ...
}

Which is easier to write? Which is easier to get 100% code coverage on? Code coverage alone may be one benefit of catching Exception directly, if you have many scenarios where you have to handle a bunch of Exception types.

However, both of those patterns are actually types of code smell. The former suggests that the developer couldn't perform many of the usual checks (e.g. a NullPointerException should never occur, because you should be able to simply say if(something != null) ...), while the latter suggests that they have no idea what the API is doing, and so are going to just catch everything and hope for the best.

Cons

The Pokemon Catch usually results in bugs, particularly regression bugs, from going unnoticed until some time after they're in production, usually right around the time users start asking "wait, why is the system behaving this way?" The more times you use Exception this way, the more risk you're inherently taking. It's preferable to have a hard crash with a stack trace than a silently debugged error that may go unnoticed for months.

The Exception

Of course, there are times when you do explicitly want to catch Exception directly. For example, a well-designed Exception-handling framework might wrap each of your methods in a consistent manner that allows solid error reporting, roll-back of partial operations, and so on. For code that advanced, I'd say it's perfectly acceptable. For the other 99.999% of code out there, it should not be used.

The Unfortunate

Unfortunately, every single line of code in the documentation either uses no error handling, or shows only the use of Exception. This is just plain wrong. While it does make it easy for novice programmers to understand what's going on, it'd be far more preferable if they used the correct data types and not abstract so much.

The Documentation

One thing that's notably absent from Apex that was a feature of early Java is the throws keyword. This keyword would force developers calling this method to catch the exception. While I'm glad we don't have that in Apex Code, because some odd 90% of the API probably throws NullPointerException, it was a nicety in terms of self-documenting code.

The documentation itself doesn't tell you which methods can throw exceptions, or which types, which means that we end up with most developers going through NPE paranoia phases; we start using try-catch around every method, because we never know what's going to happen, or (like me), we experiment with null parameters to figure out which methods will break when called with null values.

One thing I'd love to do is to introduce a list of all known methods that throw exceptions, but it would take a lot of work and a lot of maintenance, which I don't have time for. I think it'd be a pretty cool thing to have on hand, though. Even better, I'd love it if salesforce.com would do this for us. Just put a little icon in the manual next to each method that throws NPE (or some other type).

The Takeaway

Avoid catching Exception at all. Ever. It's "never" the correct solution. However, sometimes we just don't have the time to write up all the proper code coverage, or sometimes the code coverage is "impossible" to achieve if we follow best practices. In general, if you can avoid using it, do so, and if it's unavoidable, I'd recommend documenting why it's unavoidable so you have something to reference later.

  • 11
    Damn that was fast. – Adrian Larson Aug 16 '16 at 3:58
  • 5
    @AdrianLarson On a good day, I type around 100 wpm, so this post was actually quite a bit slower than it could have been (my keyboard's actually broken right now). Still, I'm glad you asked the question, because it's one of those things that really bother me when I'm reading people's code. – sfdcfox Aug 16 '16 at 4:01
  • 3
    I agree with the takeaway here. The only time I really worry about them is when building an API. Exceptions shouldn't happen, and when they do you need to know about them, not have them buried silently in a log. – Matt Lacey Aug 16 '16 at 6:21
  • 2
    I'm not sure that using a generic catch would result in uncaught bugs so long as you were using asserts in the right places in your test classes – brezotom Aug 16 '16 at 17:32
  • 3
    @brezotom I agree with that on principle. However, experience has shown me that it's often the case that developers are only interested in code coverage (just look at all the questions on here!), which means that using Exception directly is more hazardous than not. It's kind of the same argument with C's pointers; they work perfectly as long as the developer understands the consequences, but many system-crashing bugs are because they don't. It's far easier to recommend that Exception is never used, or when used, documented as to why, than to throw caution to the wind. – sfdcfox Aug 16 '16 at 17:46
23

In Java, I'd agree with the sfdcfox's takeaway. But (particularly in the absence of documentation about which Exceptions Apex will throw), catching Exception can make a lot of sense. I tend to go through various stages as the code goes through its lifecycle and end up with a hybrid where I catch some things specifically, then catch 'em all in the end.

For example, I often use the pattern of having a custom object which represents a callout which needs to be made. It will have a field like Status=(Pending, Complete, Error). A batch process looks for pending requests, makes the callout, then updates their status to show that the callout succeeded or failed.

I might start with "inadequate" exception handling in the first version. I know my method might throw CalloutException, but I don't know what else, so I leave it uncaught in the expectation that testing will crash hard and show me the errors:

try {
    doSomething();
    //Set Status__c=Complete
} catch(CalloutException e) {
    // maybe retry the callout somehow
}

Testing turns out that the external system can return results that I hadn't considered and these cause NullPointerExceptions. If possible, I update doSomething() to handle those cases and add them to my Mock class for unit testing - no change to Exception handling.

Then, testing reveals we get the occasional UNABLE_TO_LOCK_ROW error so the code becomes

try {
    doSomething();
    //Set Status__c=Complete
} catch(CalloutException e) {
    // maybe retry the callout somehow
 } catch(DmlException e) {
     if(e.getDmlType(0) == StatusCode.UNABLE_TO_LOCK_ROW) {
         retries--;
         //Do a retry
     } else {
          throw e;
     }
 }

When we finally push this to production, we can't have untrapped errors or our requests will get stuck in the queue with Status=Pending and clog up the system. So, we end up with a hybrid: first the known errors, then the Rumsfeldian unknown unknowns:

try {
    doSomething();
    //Set Status__c=Complete
} catch(CalloutException e) {
    // maybe retry the callout somehow
 } catch(DmlException e) {
     if(e.getDmlType(0) == StatusCode.UNABLE_TO_LOCK_ROW) {
         retries--;
         //Do a retry
     } else {
          throw e;
     }
 } catch(Exception e) {
     // Log e and set Status=Error
 }
  • This describes my pattern/style/sequence of events perfectly - if a specific exception doesn't have some specific amelioration (retry, partial rollback, etc) than I'd just as soon make sure the exception is caught, logged vai Platform Events, and then thrown back to higher levels to ensure database consistency. In my mind, a NPE is a coding error and doesn't need to be caught explicitly; If caught Pokemon, I get what I need via logging – cropredy 2 days ago
16

I realize I'm a "late comer" to answering this question, but it was closed by the time I saw it initially. Now that it's been reopened, I feel I have something important to add that's not been given consideration so want to add my thoughts.

Exceptions are about validating Data
Exactly what are exceptions? Exceptions are, for the most part, about validating incoming data. Ask yourself, if data fails validation, does it belong in the database? I don't believe it does. If it's allowed to go through, there must be a system in place that ensures corrective action is going to take place to fix the record that caused the exception. What will happen in some other section of code that you didn't write? It would seem to me the record would be likely to cause another exception to occur.

Database Degrades when Exceptions are caught with no action
So, in allowing a record to move forward into the database that's caused an exception, we're introducing data that is I believe is likely to cause additional problems with other code that may run operations on it. If we can't identify the cause of the exception and don't know what we're looking for, then how can we expect someone else to know how to validate the data we're passing to them? I don't believe we can.

Corrupt Data Propagates errors in other code
It says to me that either we don't understand what our code is doing or else we don't understand the nature of our data. In either case, our work is incomplete. With a true separation of concerns, our data should not be passed to other modules with dependencies. In this situation, how can a developer know if there's a dependency that exists or not? I'd assert that they can't.

Admins can't fix what they don't know to look for
Can we expect an admin to "fix" a record that throws an exception we're unable to predict will be thrown because of it's nature? How will they know what to look for? How will we be able to document repairing the exception and what to look for when it occurs? Once again, I'd assert that's something we can't do!

Could a newbie admin fix the data error?
Further, what happens when an Admin is totally new to Salesforce and has no clue about the data they're responsible for maintaining? What do you think will happen to the quality of the the Org's database in those situations?

What is the cost of a customer's data?
The most valuable thing a Salesforce customer has is their database. It's very expensive for them to create, maintain and/or reproduce. The last thing we as developers want to allow to happen is for their database to become "corrupted" with incomplete, inaccurate or erroneous data. When we allow data to be inserted to the database that's caused an "anonymous exception" we've caught, to me, that's exactly what we're doing. We're degrading the quality of their very costly and expensive database they've invested lots of money in.

What constitutes adequate exception handling?
Writing to a log is an inadequate solution. Who reads them and takes action? Logs don't directly alert an admin to tell them to take action and investigate what might be in out of bounds in a record that would cause the exception to occur. Sending an email to the Admin is at a minimum, what I believe is appropriate for a developer to do when they catch an exception of any kind which they're unable to correct with code; let alone a generic "pokeman" exception.

Would you bet your pension on data filled with exceptions?
The essence of what I'm conveying here is that in my view, to simply catch the exception and allow the data to pass is irresponsible on our part and can be very costly to the customer who owns the org. What happens when they rely on that data for analysis to make decisions in their business? Would you want to rely on a database that contained questionable data to make multimillion dollar decisions? I certainly wouldn't and I can't imagine customers wanting to do that either.

I think I've "beaten the dead horse" enough to make my point as to why I believe this is not a good practice and why it's not one I would use.

  • I upvoted because I appreciate the perspective, and it is a good one to share. Thank you! I do think, though, that if you read my question you will find that I wasn't asking about data at all, or about handling logic, but specifically about catching known types instead of one generic. – Adrian Larson Aug 30 '16 at 15:07
  • 1
    I may not have been clear on this, but I've no issues with catching known types of exceptions provided you have a plan for what you're going to do with them once you catch them. The issue is that we need to know what to do with them once caught. They affect the database & as developers we can't ignore that reality. It's something we need to be conscious of because I guarantee you that it's of the highest priority to our clients. If they're generic and we can't do something through handling, then that's an issue that degrades the data quality. – crmprogdev Aug 30 '16 at 15:27
  • Right, that behavior is very important. But my question was whether catching exceptions of an unknown type is acceptable practice. Like I said, what you've written is great. It just seems somewhat tangential on the surface. – Adrian Larson Aug 30 '16 at 15:29
  • I don't think you can unlink the two issues. That's why I don't believe it's tangential. – crmprogdev Aug 30 '16 at 15:45
11

Here are my views on this:

Is it ever acceptable (or even preferable) to catch a generic Exception in Apex?

Yes, there are definitely situations where a generic Exception is acceptable or preferred.

Prerequisite conditions:

  • You don't want to show ugly error messages to the end user.
  • You have logging provisions in your system.

Situation

  1. You are working on AppExchange app and don't want to show ugly error messages. Instead, you would want to log error messages and report them to your support team for further investigation

  2. You are working on a large maintenance project and are not sure of all the possible exceptions that may come up.

  3. The business demands it. Sometimes product owners demand that no errors should be shown to the end user. Instead, it should just be logged. In such scenarios, we can use generic catch blocks.

    catch(Exception e) { /Code to log exception/ }

Are there repercussions to watch out for?

Yes, definitely.

  1. We should have proper logs of exceptions with enough details so that developers will be able to debug it at a later point in time.

  2. We should define the scope carefully. If we define catch at the root method, then we may miss specific details at the method level.

  • 6
    Using only debug is essentially an empty catch block. – Adrian Larson Aug 16 '16 at 4:52
  • Updated the answer – AtulRajguru9 Aug 16 '16 at 6:15
  • 2
    Not only is it an empty catch block, but if it's a one time error and you didn't have logging turned on, you may never find out what the error was. – brezotom Aug 16 '16 at 17:35
0

Exception handlers have two main purposes:

  1. provide mechanism to recover from exceptions
  2. provide additional information for the cause of exception.

In a 'catch all' handler, you have no idea what was is the context of the exception condition or how to recover from it. By catching and not re-throwing it, you are essentially saying that it is safe to ignore the exception. You kick the bucket down the road and make it much harder for you to detect and debug real errors in your code when they manifest in exotic ways -- like data corruption.

In Java/C++ it is common to use catch all statements as a final wrapper around thread execution to capture additional application state prior terminating a program from an unexpected and unrecoverable exception. In Apex, most of the state is in the DB, not in memory, so this is hardly justifiable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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