ABAP Exception Classes: Create Custom Exceptions

Category
ABAP-Statements
Published
Author
Johannes

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 ClassRAISING requiredTypical Use
CX_STATIC_CHECKYesBusiness exceptions
CX_DYNAMIC_CHECKOptionalTechnical errors
CX_NO_CHECKNoCritical 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 chain
TRY.
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.
" Usage
RAISE 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 expression
DATA(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 SWITCH
DATA(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 usage
CLASS 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 exceptions
CLASS 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 exceptions
CLASS 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 hierarchy
TRY.
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 exception
RAISE EXCEPTION TYPE zcx_with_message
EXPORTING
textid = zcx_with_message=>field_invalid
iv_field = 'KUNNR'.
" Retrieve text
CATCH 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 RESUME
TRY.
process_values( ).
CATCH BEFORE UNWIND zcx_resumable_error INTO DATA(lx_error).
WRITE: / 'Warning: Negative value', lx_error->mv_value.
RESUME. " Continue after RAISE RESUMABLE
ENDTRY.

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.
" Usage
RAISE EXCEPTION zcx_factory=>not_found(
iv_entity = 'Customer'
iv_id = '12345'
).

Creating Exception Class in SE24

  1. SE24 → Create class (name starts with ZCX_ or YCX_)
  2. Superclass: CX_STATIC_CHECK (or other)
  3. Interfaces: Add IF_T100_MESSAGE
  4. Attributes: Define error data (e.g., MV_ORDER_ID)
  5. Texts: Link exception IDs with message class
  6. 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/SWITCH for inline exceptions.
  • RESUMABLE for warnings that can be skipped.
  • Exception texts in message class for translatability.
  • get_text() for formatted error message.