Skip to Content
Technical Articles

Why ABAP exception handling isn’t like playing catch with your dog

I’ve been cleaning up some legacy ABAP code lately, and a reoccurring theme is poor exception handling. In this post, I’ll explain why ABAP exception handling shouldn’t be treated like a game of catch with your dog. I’ll discuss some anti-patterns I’ve come across in the legacy code and give a few suggestions on how to improve your exception handling.

Catching CX_ROOT

Catching CX_ROOT looks something like this:

    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_root INTO DATA(lx_root).
        " do something, like logging the error;
        " or even worse, do nothing
    ENDTRY.

CX_ROOT is an abstract class and the super class of all exception classes. Catching CX_ROOT thus catches every possible exception that could be raised. This is a very broad approach to exception handling, which I’m not a big fan of. When you play catch with your dog, you always want the dog to catch all the balls. This is not an appropriate approach to ABAP exception handling and was the major inspiration for writing this blog post.

I think that exception handling should be as narrow and specific as possible. Hopefully zcl_my_class=>my_method( ) only raises one specific exception, and the caller should only catch this exception.

I find it hard to imagine scenarios where it makes sense to catch every possible exception, especially when the CATCH clause is empty. Why would you want your code to continue executing when an unknown exception has occurred?

Catching an exception just to raise it again

I’ve come across code which looks something like this:

    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_my_exception INTO DATA(lx_my_exception).
        RAISE EXCEPTION lx_my_exception.
    ENDTRY.

To catch an exception just to raise the same exception again makes no sense. It is much better to make sure that the exception is part of the method signature and just let the exception bubble up without catching it.

The only scenario I can think of where it would make sense to catch the exception to then raise it again is the scenario where the exception is logged first:

    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_my_exception INTO DATA(lx_my_exception).
        zcl_my_log_class=>write_to_log( lx_my_exception ).
        RAISE EXCEPTION lx_my_exception.
    ENDTRY.

Not having the exception in the method signature

I’ve seen code where exceptions are raised, which are not part of the method signature. This makes the code fragile since the callers of the method don’t know which exceptions to expect and thus don’t see the need to implement proper exception handling on their end.

Raising several different types of exceptions from the same method

I’ve seen methods with several exceptions in the method signature. The caller of the method would then have to catch several exceptions:

    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_my_exception1.
        ...
      CATCH cx_my_exception2.
        ...
    ENDTRY.

If a method raises several exceptions, it is probably a sign of the method doing several things. Consider Clean ABAP: Do one thing, do it well, do it only.

If zcl_my_class=>my_method( ) raises several exceptions, a refactoring should be done (see Clean ABAP: Throw one type of exception).

Catch is empty

Another scenario I’ve seen is that the exception is caught, but not acted upon, especially with an entertaining comment about how this should never happen:

    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_my_exception INTO DATA(lx_my_exception).
        " do nothing, since this should never happen...
    ENDTRY.

Maybe there is a scenario where the exception shouldn’t be acted upon, but in that case, I would recommend to:

  • Add a comment explaining why it is not necessary to act upon the exception.
  • Not catch the exception into an object instance, when it is not being used.
    TRY.
      zcl_my_class=>my_method( ).
      CATCH cx_my_exception.
        " No action required since ...
    ENDTRY.

The TRY-CATCH block is several hundred lines big

A violation of the recommendation Clean ABAP: Keep methods small is to write huge blocks of code and then surrounding these huge blocks of code with a TRY-CATCH. Please keep your methods small, and the exception handling will be much easier to follow.

Conclusion

The anti-patterns discussed above don’t make up an exhaustive list of poor exception handling but illustrate some common pitfalls which should be avoided. If you have more examples, please feel free to add them in the comments section.

Happy exception handling!

This blog post first appeared on the Developer Voyage blog at https://www.developervoyage.com/2020/03/27/why-abap-exception-handling-isnt-like-playing-catch-with-your-dog.html

9 Comments
You must be Logged on to comment or reply to a post.
  • Good stuff.

    I have seen an issue where an exception was thrown “resolved” with

    TRY.
    CATCH cx_root.
    ENDTRY.

    I did raise merry heck about it.

    I have used catch cx_root once. This was in a BW extractor getting the training data (LSO) for each of 70000+ employees from the HR module on an ECC system. It was necessary, because we could not abort the whole extraction due to one error. The errors could arise from the data and there was no feasible way of error checking every possible error that could be thrown by the standard function modules and methods before trying to get the data.

    The catch was written to ensure that the appropriate people were notified if an error happened, with as much detail as possible about what the error was.

     

     

    • Thanks! I understand that you got upset with the first scenario you describe. And thanks for providing the second example, where it actually was necessary to catch cx_root. It sounds like you implemented the best possible solution under the circumstances.

  • Good blog, just two thoughts:

    • An exception (derived from CX_STATIC_CHECK or CX_DYNAMIC_CHECK) missing from the signature will not be propagated to the caller, but cause a run-time error instead if remains unhandled.

      For some rare, unrecoverable cases it could make sense to intentionally generate a dump via an exception.

    • Multiple handlers can be useful if you are dealing with exceptions designed via inheritance. Here the order from specific to generic must be ensured:
    CLASS foo DEFINITION.
    
      PUBLIC SECTION.
        METHODS do_some_alv_stuff
          RAISING
            cx_salv_error.
    
    ENDCLASS.
    
    TRY.
      
        NEW foo( )->do_some_alv_stuff( ).
      
      CATCH cx_salv_wrong_call.
        
       " Most specifc handling
        
      CATCH cx_salv_access_error.
        
       " Less specific handling
      
      CATCH cx_salv_error.
    
       " Least specific handling
    
    ENDTRY.

     

  • Thanks for this important post.

    For the matter of fact, I’m a big fan of not catching unexpected exceptions instead of catching/logging them. From my experience, a dump will be reported (and fixed) quickly, while a nice message/log can be ignored for ages without anyone is being informed about the problem.

  • Exception handling is difficult, my main requirement is that it should be readable, class based exceptions are mandatory for bigger developments where we have the benefits, but in other cases I’m in favor of KISS.

    What is everyone’s thought on raising class based exceptions within a method and catching it in the method itself again?

    • I’ve raised a classed based exception within a method to catch it in the method itself again in some cases. I don’t think it is generally wrong to do so.