OOP ABAP
ABAP Developmentintermediate

OOP ABAP Part 4: Class-Based Exceptions

Key takeaway

Handle errors the class-based way: TRY/CATCH/CLEANUP, RAISE EXCEPTION TYPE, the CX STATIC CHECK / CX DYNAMIC CHECK / CX NO CHECK hierarchy, custom exception classes with message texts, and how they compare with classic EXCEPTIONS and sy-subrc.

Explanation

📌 KEY POINTS
✅ Class-based exceptions are objects: RAISE EXCEPTION TYPE zcx_… and handle them with TRY … CATCH … ENDTRY.

✅ Three kinds: CX_STATIC_CHECK (must be declared/handled), CX_DYNAMIC_CHECK (runtime-checked), CX_NO_CHECK (system-like, never declared).

✅ Custom exception classes (ZCX_…) can carry T100 message texts and attributes.

⚠️ Classic EXCEPTIONS (sy-subrc) and class-based exceptions cannot be mixed in one method signature.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Methods in Parts 1–3 wrote errors to the list. Reusable code must instead signal errors to the caller.

━━━━━━━━━━━━━━━━━━━━
▶ 1. CLASSIC ERROR HANDLING (FOR COMPARISON)

Function modules declare EXCEPTIONS not_found = 1 and the caller checks sy-subrc after CALL FUNCTION. It still exists in every system (BAPIs use a RETURN table instead). It is easy to forget the sy-subrc check, and no details travel with the error.

━━━━━━━━━━━━━━━━━━━━
▶ 2. TRY / CATCH / CLEANUP

TRY.

lo_service->book( … ).
CATCH zcx_flight_error INTO lx_error.

MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.

CLEANUP.

" runs when the exception leaves this TRY block unhandled

ENDTRY.

  • CATCH blocks are checked in order — catch specific classes before general ones (CX_ROOT last).
  • INTO lx_error gives the exception object: get_text( ), previous, custom attributes.

━━━━━━━━━━━━━━━━━━━━
▶ 3. THE THREE CATEGORIES

  • CX_STATIC_CHECK — the compiler checks: a method must declare it with RAISING or handle it. Use for expected business errors (flight full, customer blocked).
  • CX_DYNAMIC_CHECK — declaration only checked at runtime; for errors callers can usually prevent (e.g. CX_SY_ZERODIVIDE).
  • CX_NO_CHECK — may occur anywhere, never declared (e.g. resource shortages).

━━━━━━━━━━━━━━━━━━━━
▶ 4. CUSTOM EXCEPTION CLASS

SE24: create ZCX_FLIGHT_ERROR, superclass CX_STATIC_CHECK, tick "With Message Class". Add interface IF_T100_MESSAGE texts mapping to message class ZFLIGHT (e.g. FLIGHT_FULL = message 001 with &1 &2). Attributes (MV_CARRID, MV_CONNID) fill the placeholders.

━━━━━━━━━━━━━━━━━━━━
▶ 5. RAISING

RAISE EXCEPTION TYPE zcx_flight_error EXPORTING textid = zcx_flight_error=>flight_full mv_carrid = iv_carrid mv_connid = iv_connid.

💡 Modern (7.50+): RAISE EXCEPTION NEW zcx_flight_error( … ) and MESSAGE e001(zflight) WITH … in RAISE EXCEPTION … MESSAGE.

━━━━━━━━━━━━━━━━━━━━
▶ 6. CHAINING

Catch a technical exception and raise a business one with previous = lx_technical to keep the root cause.

━━━━━━━━━━━━━━━━━━━━
▶ 7. DO NOT

  • Catch CX_ROOT everywhere and ignore it.
  • Mix MESSAGE type E inside reusable methods — raise an exception instead.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. Create ZCX_FLIGHT_ERROR as in the code comments.
  2. Run the report: book a full flight, see the message text from ZFLIGHT.
  3. Add a CLEANUP block and observe when it runs.

✅ CHECKPOINT: your methods signal errors with exceptions, and callers handle them explicitly.

Code example

ABAP Code
* ===== Exception class ZCX_FLIGHT_ERROR (SE24, superclass CX_STATIC_CHECK, with message class) =====* Texts (IF_T100_MESSAGE):*   FLIGHT_FULL      -> ZFLIGHT 001 'Flight &1 &2 is fully booked'*   FLIGHT_NOT_FOUND -> ZFLIGHT 002 'Flight &1 &2 does not exist'* Attributes: MV_CARRID TYPE S_CARR_ID, MV_CONNID TYPE S_CONN_ID (used as &1, &2) REPORT zoop_exceptions. CLASS lcl_booking DEFINITION.  PUBLIC SECTION.    METHODS book      IMPORTING iv_carrid TYPE s_carr_id                iv_connid TYPE s_conn_id                iv_fldate TYPE s_date      RAISING   zcx_flight_error.                 " static check: must be declaredENDCLASS. CLASS lcl_booking IMPLEMENTATION.  METHOD book.    DATA ls_flight TYPE sflight.    SELECT SINGLE * FROM sflight INTO ls_flight      WHERE carrid = iv_carrid AND connid = iv_connid AND fldate = iv_fldate.    IF sy-subrc <> 0.      RAISE EXCEPTION TYPE zcx_flight_error        EXPORTING textid    = zcx_flight_error=>flight_not_found                  mv_carrid = iv_carrid                  mv_connid = iv_connid.    ENDIF.    IF ls_flight-seatsocc >= ls_flight-seatsmax.      RAISE EXCEPTION TYPE zcx_flight_error        EXPORTING textid    = zcx_flight_error=>flight_full                  mv_carrid = iv_carrid                  mv_connid = iv_connid.    ENDIF.    " ... create booking ...  ENDMETHOD.ENDCLASS. PARAMETERS: p_carr TYPE s_carr_id DEFAULT 'LH',            p_conn TYPE s_conn_id DEFAULT '0400',            p_date TYPE s_date. DATA: lo_booking TYPE REF TO lcl_booking,      lx_flight  TYPE REF TO zcx_flight_error,      lx_root    TYPE REF TO cx_root,      lv_text    TYPE string,      lv_result  TYPE i. START-OF-SELECTION.  CREATE OBJECT lo_booking. * ===== Specific exceptions first, general last =====  TRY.      lo_booking->book( iv_carrid = p_carr iv_connid = p_conn iv_fldate = p_date ).      WRITE: / 'Booking created'.    CATCH zcx_flight_error INTO lx_flight.      lv_text = lx_flight->get_text( ).      WRITE: / 'Business error:', lv_text.    CATCH cx_root INTO lx_root.      lv_text = lx_root->get_text( ).      WRITE: / 'Unexpected error:', lv_text.  ENDTRY. * ===== System exception (dynamic check) =====  TRY.      lv_result = 10 / 0.    CATCH cx_sy_zerodivide.      WRITE: / 'Division by zero caught'.  ENDTRY. * ===== Classic FM exceptions for comparison =====* CALL FUNCTION 'Z_FLIGHT_READ'*   EXPORTING iv_carrid = p_carr*   EXCEPTIONS not_found = 1 OTHERS = 2.* IF sy-subrc <> 0.*   MESSAGE ID sy-msgid TYPE 'I' NUMBER sy-msgno WITH sy-msgv1 sy-msgv2.* ENDIF.

Real project scenario

A reusable pricing class issued MESSAGE type E when a condition was missing. When the class was later called from a background interface and an OData service, the MESSAGE caused job terminations and unreadable HTTP errors.
The team replaced it with ZCX_PRICING_ERROR (CX_STATIC_CHECK with T100 texts). The report shows the text as a message, the OData layer maps it to a proper error response, and the background job logs it in the application log and continues with the next document.

Common mistakes

  • MESSAGE type E inside reusable methods.
  • Catching CX_ROOT and doing nothing.
  • CATCH of a superclass before a subclass (subclass block never reached).
  • Not declaring static-check exceptions with RAISING.
  • Losing the root cause instead of using previous.

Best practices

  • Use CX_STATIC_CHECK subclasses for expected business errors.
  • Put texts in a message class via IF_T100_MESSAGE.
  • Catch specific exceptions first; handle at the layer that can react.
  • Chain technical causes with previous.
  • Map exceptions to the channel: message in UI, BAPIRET2 in RFC, error response in OData.

Interview angle

Questions: "Static vs dynamic vs no-check exceptions?", "Classic EXCEPTIONS vs class-based?", "What does CLEANUP do?"
Strong answer: class-based exceptions are objects raised with RAISE EXCEPTION and caught with TRY/CATCH; CX_STATIC_CHECK must be declared or handled (compile-time check), CX_DYNAMIC_CHECK checked at runtime, CX_NO_CHECK never declared; custom classes carry T100 texts and attributes; CLEANUP runs when the exception propagates out; classic EXCEPTIONS use sy-subrc and cannot be mixed in one signature.

Practice this lesson

Quick check

1. Two CATCH blocks follow a TRY: CATCH cx_root first, then CATCH zcx_flight_error. What happens when zcx_flight_error is raised?

2. A save method catches a database exception. How should it pass the error on without losing the root cause?

3. Which exception type must be declared with RAISING?

Interview questions

1. A service catches CX_ROOT and raises a generic "Save failed" error, so support cannot see whether it was a locking or SQL problem. How should exceptions be handled instead?

Answer it in your own words first, then compare.

2. What are static-check and dynamic-check exceptions?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics