Exception Handling
All exception classes inherit from CX_ROOT through one of three categories:
23.1 Class-based exceptions
All exception classes inherit from CX_ROOT through one of three categories:
Must be declared in RAISING?
Checked by compiler Yes (statically) Expected errors the caller must handle Yes, but checked only at runtime
Errors the caller can avoid (e.g. CX_SY_ZERODIVIDE) No (implicitly declared)
Fatal/system errors (e.g. resource exhaustion)
23.2 TRY / CATCH / CLEANUP
CATCH order matters: list subclasses before superclasses (CX_ROOT last).
CATCH BEFORE UNWIND keeps the context alive (for resumable exceptions).
RETRY (in a CATCH block) restarts the TRY block; RESUME continues after a RAISE RESUMABLE EXCEPTION.
lx->get_text( ), lx->get_longtext( ), lx->previous (chained cause), lx->get_source_position( ).
TRY.
DATA(lv_result) = 10 / lv_divisor.
DATA(ls_row) = lt_orders[ order_id = '999' ].
lo_account->withdraw( 1000 ).
CATCH cx_sy_zerodivide INTO DATA(lx_div).
MESSAGE lx_div->get_text( ) TYPE 'I'.
CATCH cx_sy_itab_line_not_found.
MESSAGE 'Order not found' TYPE 'I'.
CATCH zcx_insufficient_funds cx_sy_arithmetic_overflow INTO DATA(lx_any).
MESSAGE lx_any TYPE 'I'.
CLEANUP.
" runs when an exception leaves this TRY block without being caught here
lo_resource->close( ).
ENDTRY.23.3 Defining an exception class
In SE24/ADT create ZCX_INSUFFICIENT_FUNDS inheriting from CX_STATIC_CHECK, implementing IF_T100_MESSAGE (or IF_T100_DYN_MSG) to use message-class texts:
CLASS zcx_insufficient_funds DEFINITION PUBLIC
INHERITING FROM cx_static_check FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_t100_dyn_msg.
DATA mv_amount TYPE string READ-ONLY.
METHODS constructor
IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
iv_amount TYPE string OPTIONAL.
ENDCLASS.
CLASS zcx_insufficient_funds IMPLEMENTATION.
METHOD constructor ##ADT_SUPPRESS_GENERATION.
super->constructor( previous = previous ).
mv_amount = iv_amount.
CLEAR me->textid.
IF textid IS INITIAL.
if_t100_message~t100key = VALUE #( msgid = 'ZBANK' msgno = '001' attr1 = 'MV_AMOUNT' ).
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
ENDCLASS.23.4 Raising exceptions
RAISE EXCEPTION TYPE zcx_insufficient_funds EXPORTING iv_amount = |{ iv_amount }|. " classic
RAISE EXCEPTION NEW zcx_insufficient_funds( iv_amount = |{ iv_amount }| ). " 7.52+
" With message directly (IF_T100_DYN_MSG)
RAISE EXCEPTION TYPE zcx_order_error MESSAGE e005(zorders) WITH lv_order_id.
" In expressions
DATA(lv_x) = COND #( WHEN lv_ok = abap_true THEN 1 ELSE THROW zcx_order_error( ) ).
" Wrap a lower-level exception
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
RAISE EXCEPTION NEW zcx_order_error( previous = lx_db ).23.5 Classic (non-class-based) exceptions
Function modules and old methods use EXCEPTIONS and set sy-subrc:
Class-based and classic exceptions cannot be mixed in one procedure's interface. sy-subrc is also the classic return code for statements (READ TABLE, SELECT, AUTHORITY-CHECK, …): 0 = success.
" Inside a function module
RAISE not_found. " ends FM, caller gets sy-subrc
MESSAGE e001(zmsg) RAISING not_found. " sets sy-msg* too
" Caller
CALL FUNCTION 'Z_FM' EXCEPTIONS not_found = 1 OTHERS = 2.23.6 Runtime errors
Uncaught exceptions produce a short dump (ST22) with the source position, call stack, and variable values. Catchable runtime errors are mapped to CX_SY_* classes (e.g. CX_SY_CONVERSION_NO_NUMBER, CX_SY_OPEN_SQL_DB, CX_SY_REF_IS_INITIAL). MESSAGE … TYPE 'X' deliberately dumps.
23.7 Guidelines
Use class-based exceptions in new code; one exception class per error family with texts from message classes.
Don't catch CX_ROOT and swallow it silently; log it (application log, BAL_* function modules or CL_BALI_LOG in ABAP Cloud).
Prefer CX_STATIC_CHECK for business errors, CX_NO_CHECK sparingly.
Convert technical exceptions to meaningful business exceptions at layer boundaries, keeping previous.
🧪 Part II Exercises
Build a hashed table of 100,000 generated customers and compare the runtime of key lookups against a standard table using GET RUN TIME FIELD.
Given a table of sales (region, product, amount), use LOOP AT … GROUP BY to print totals per region and per region/product; repeat with AT NEW and compare.
Using only VALUE, FOR, REDUCE, and FILTER, compute: the list of product names in upper case, the total revenue, and the sales above the average.
Write a function module Z_CALC_ORDER_TOTAL with class-based exceptions and call it from a report.
Write an ABAP SQL query that joins SFLIGHT, SPFLI, and SCARR to list, per carrier name, the number of flights and the average occupancy percentage, ordered descending.
Create a lock object for ZORDERS and a report that locks an order, waits for user confirmation (popup), changes the status, commits, and unlocks. Open it twice to observe the lock in SM12.
Build a selection-screen report with a mandatory carrier, a date range defaulting to the last 30 days, radio buttons for ALV vs. list output, and validation in AT SELECTION-SCREEN.
Display the result of exercise 7 with CL_SALV_TABLE, including totals, a hidden column, renamed column headers, and a double-click handler.
Model Employee → Manager / Developer with an abstract calculate_bonus method and an interface lif_reportable; process a mixed table polymorphically.
Create a custom exception class with a message-class text and attributes; raise it from a method, catch it two levels up, and display the message with its previous chain.