Exception Classes enable structured error handling in ABAP. Custom exceptions (CX_ classes) transport error data, support multi-language texts and integrate seamlessly with TRY-CATCH.
Exception Hierarchy
CX_ROOT │ ├── CX_STATIC_CHECK " Must be declared (RAISING) │ └── CX_DYNAMIC_CHECK " Can but doesn't have to be declared │ └── CX_NO_CHECK " Doesn't have to be declared (Runtime)| Base Class | RAISING required | Typical Use |
|---|---|---|
CX_STATIC_CHECK | Yes | Business exceptions |
CX_DYNAMIC_CHECK | Optional | Technical errors |
CX_NO_CHECK | No | Critical system errors |
Creating Exception Classes
1. Simple Exception (SE24/ADT)
CLASS zcx_validation_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. " Interface for texts INTERFACES: if_t100_message. INTERFACES: if_t100_dyn_msg.
" Constants for message texts CONSTANTS: BEGIN OF invalid_input, msgid TYPE symsgid VALUE 'ZMSG', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_FIELD', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF invalid_input.
" Attributes for error data DATA: mv_field TYPE string READ-ONLY.
" Constructor METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_field TYPE string OPTIONAL.
ENDCLASS.
CLASS zcx_validation_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ).
mv_field = iv_field.
CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = invalid_input. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD.ENDCLASS.2. Exception with Multiple Texts
CLASS zcx_order_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. INTERFACES: if_t100_message.
" Different error texts CONSTANTS: BEGIN OF order_not_found, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_ORDER_ID', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF order_not_found,
BEGIN OF order_already_shipped, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '002', attr1 TYPE scx_attrname VALUE 'MV_ORDER_ID', attr2 TYPE scx_attrname VALUE 'MV_SHIP_DATE', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF order_already_shipped,
BEGIN OF invalid_quantity, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '003', attr1 TYPE scx_attrname VALUE 'MV_QUANTITY', attr2 TYPE scx_attrname VALUE 'MV_MAX_QUANTITY', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF invalid_quantity.
" Attributes DATA: mv_order_id TYPE string READ-ONLY, mv_ship_date TYPE d READ-ONLY, mv_quantity TYPE i READ-ONLY, mv_max_quantity TYPE i READ-ONLY.
METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_order_id TYPE string OPTIONAL iv_ship_date TYPE d OPTIONAL iv_quantity TYPE i OPTIONAL iv_max_quantity TYPE i OPTIONAL.
ENDCLASS.
CLASS zcx_order_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ).
mv_order_id = iv_order_id. mv_ship_date = iv_ship_date. mv_quantity = iv_quantity. mv_max_quantity = iv_max_quantity.
CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = order_not_found. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD.ENDCLASS.3. Raise Exception (RAISE EXCEPTION)
CLASS lcl_order_service DEFINITION. PUBLIC SECTION. METHODS: get_order IMPORTING iv_order_id TYPE string RETURNING VALUE(rs_order) TYPE ty_order RAISING zcx_order_error.
METHODS: ship_order IMPORTING iv_order_id TYPE string RAISING zcx_order_error.ENDCLASS.
CLASS lcl_order_service IMPLEMENTATION. METHOD get_order. " Search for order SELECT SINGLE * FROM ztorders WHERE order_id = @iv_order_id INTO @rs_order.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_not_found iv_order_id = iv_order_id. ENDIF. ENDMETHOD.
METHOD ship_order. DATA: ls_order TYPE ty_order.
ls_order = get_order( iv_order_id ).
IF ls_order-status = 'SHIPPED'. RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_already_shipped iv_order_id = iv_order_id iv_ship_date = ls_order-ship_date. ENDIF.
" Process shipping... ENDMETHOD.ENDCLASS.4. Catch and Process Exception
DATA: lo_service TYPE REF TO lcl_order_service, ls_order TYPE ty_order.
lo_service = NEW #( ).
TRY. ls_order = lo_service->get_order( '12345' ).
CATCH zcx_order_error INTO DATA(lx_error). " Output error text DATA(lv_message) = lx_error->get_text( ). WRITE: / 'Error:', lv_message.
" Access specific attributes WRITE: / 'Order ID:', lx_error->mv_order_id.
" Re-throw error " RAISE EXCEPTION lx_error.ENDTRY.5. Exception Chain (PREVIOUS)
METHOD process_order. TRY. " Database operation validate_order( is_order ).
CATCH zcx_validation_error INTO DATA(lx_val). " Pass original exception as PREVIOUS RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_not_found previous = lx_val iv_order_id = is_order-id. ENDTRY.ENDMETHOD.
" When catching: Traverse chainTRY. process_order( ls_order ).
CATCH cx_root INTO DATA(lx_error). " Output all exceptions in the chain DATA(lx_current) = lx_error.
WHILE lx_current IS BOUND. WRITE: / lx_current->get_text( ). lx_current = lx_current->previous. ENDWHILE.ENDTRY.6. Exception without Message Class
CLASS zcx_simple_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. " Simple text without T100 DATA: mv_message TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_message TYPE string OPTIONAL previous LIKE previous OPTIONAL.
METHODS: if_message~get_text REDEFINITION.
ENDCLASS.
CLASS zcx_simple_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ). mv_message = iv_message. ENDMETHOD.
METHOD if_message~get_text. result = mv_message. ENDMETHOD.ENDCLASS.
" UsageRAISE EXCEPTION TYPE zcx_simple_error EXPORTING iv_message = 'An error occurred'.7. COND/SWITCH with THROW
DATA: lv_age TYPE i VALUE -5.
" Throw exception in expressionDATA(lv_status) = COND string( WHEN lv_age >= 0 AND lv_age < 18 THEN 'Minor' WHEN lv_age >= 18 THEN 'Adult' ELSE THROW zcx_validation_error( textid = zcx_validation_error=>invalid_input iv_field = 'AGE' )).
" With SWITCHDATA(lv_result) = SWITCH string( lv_status WHEN 'A' THEN 'Active' WHEN 'I' THEN 'Inactive' ELSE THROW zcx_validation_error( iv_field = 'STATUS' )).8. Local Exception Class
" For tests or local usageCLASS lcx_local_error DEFINITION INHERITING FROM cx_static_check.
PUBLIC SECTION. DATA: mv_info TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_info TYPE string OPTIONAL previous LIKE previous OPTIONAL.
METHODS: if_message~get_text REDEFINITION.ENDCLASS.
CLASS lcx_local_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ). mv_info = iv_info. ENDMETHOD.
METHOD if_message~get_text. result = |Local error: { mv_info }|. ENDMETHOD.ENDCLASS.9. Exception Hierarchy for Domain
" Base class for all order exceptionsCLASS zcx_order_base DEFINITION PUBLIC ABSTRACT INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. DATA: mv_order_id TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_order_id TYPE string OPTIONAL previous LIKE previous OPTIONAL.ENDCLASS.
" Specific exceptionsCLASS zcx_order_not_found DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
CLASS zcx_order_locked DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
CLASS zcx_order_invalid_state DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
" Catch by hierarchyTRY. process_order( ).
CATCH zcx_order_not_found INTO DATA(lx_not_found). " Specific handling
CATCH zcx_order_locked INTO DATA(lx_locked). " Specific handling
CATCH zcx_order_base INTO DATA(lx_order). " All other order exceptions
CATCH cx_static_check INTO DATA(lx_other). " All other static exceptions
ENDTRY.10. MESSAGE Integration
" Message class ZMSG with message 001:" "Field & contains invalid value"
CLASS zcx_with_message DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. INTERFACES: if_t100_message. INTERFACES: if_t100_dyn_msg.
CONSTANTS: BEGIN OF field_invalid, msgid TYPE symsgid VALUE 'ZMSG', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_FIELD', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF field_invalid.
DATA: mv_field TYPE string READ-ONLY.
METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_field TYPE string OPTIONAL.
ENDCLASS.
" Throw exceptionRAISE EXCEPTION TYPE zcx_with_message EXPORTING textid = zcx_with_message=>field_invalid iv_field = 'KUNNR'.
" Retrieve textCATCH zcx_with_message INTO DATA(lx_error). " get_text() returns: "Field KUNNR contains invalid value" DATA(lv_msg) = lx_error->get_text( ).
" Or as MESSAGE MESSAGE lx_error TYPE 'I'.11. RESUMABLE Exceptions
CLASS zcx_resumable_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. DATA: mv_value TYPE i READ-ONLY.
METHODS: constructor IMPORTING iv_value TYPE i OPTIONAL previous LIKE previous OPTIONAL.ENDCLASS.
METHOD process_values. LOOP AT lt_values INTO DATA(lv_value). IF lv_value < 0. " RESUMABLE: Can be continued with RESUME RAISE RESUMABLE EXCEPTION TYPE zcx_resumable_error EXPORTING iv_value = lv_value. ENDIF.
" Further processing... ENDLOOP.ENDMETHOD.
" Call with RESUMETRY. process_values( ).
CATCH BEFORE UNWIND zcx_resumable_error INTO DATA(lx_error). WRITE: / 'Warning: Negative value', lx_error->mv_value. RESUME. " Continue after RAISE RESUMABLEENDTRY.12. Best Practice: Exception Factory
CLASS zcx_factory DEFINITION. PUBLIC SECTION. CLASS-METHODS: not_found IMPORTING iv_entity TYPE string iv_id TYPE string RETURNING VALUE(ro_exception) TYPE REF TO zcx_not_found,
validation_failed IMPORTING iv_field TYPE string iv_message TYPE string RETURNING VALUE(ro_exception) TYPE REF TO zcx_validation_error.ENDCLASS.
CLASS zcx_factory IMPLEMENTATION. METHOD not_found. ro_exception = NEW #( iv_entity = iv_entity iv_id = iv_id ). ENDMETHOD.
METHOD validation_failed. ro_exception = NEW #( iv_field = iv_field iv_message = iv_message ). ENDMETHOD.ENDCLASS.
" UsageRAISE EXCEPTION zcx_factory=>not_found( iv_entity = 'Customer' iv_id = '12345').Creating Exception Class in SE24
- SE24 → Create class (name starts with
ZCX_orYCX_) - Superclass:
CX_STATIC_CHECK(or other) - Interfaces: Add
IF_T100_MESSAGE - Attributes: Define error data (e.g.,
MV_ORDER_ID) - Texts: Link exception IDs with message class
- Constructor: Define parameters for attributes
Important Notes / Best Practice
- CX_STATIC_CHECK for business exceptions (must be declared in RAISING).
- Custom hierarchy for business domains (e.g.,
ZCX_ORDER_BASE). - IF_T100_MESSAGE for multi-language texts with message class.
- PREVIOUS for exception chains (trace root cause).
- READ-ONLY for exception attributes (immutability).
- Catch specific exceptions first, then more general ones.
- THROW in
COND/SWITCHfor inline exceptions. - RESUMABLE for warnings that can be skipped.
- Exception texts in message class for translatability.
- get_text() for formatted error message.