MOVE_CAST_ERROR¶
A CAST operator failed because the object is not an instance of the target class.
The CAST operator tried to narrow an object reference to a more specific class, but the actual object at runtime is not an instance of that class (or any of its subclasses).
Symptom¶
ST22 shows:
Runtime Errors MOVE_CAST_ERROR
Exception CX_SY_MOVE_CAST_ERROR
Short text Move with type conversion error (class cast).
The dump line is the CAST expression.
Cause¶
CAST is a downcast — it moves from a superclass reference to a subclass reference. If the actual runtime object is not of the target type, it dumps.
Common triggers:
- Forgetting to check the actual runtime type before casting.
- A factory method returning different subclass instances depending on configuration.
- Reusing a reference variable that holds different object types in different code paths.
Reproduce¶
REPORT z_move_cast_error.
START-OF-SELECTION.
" CX_SY_ZERODIVIDE is a subclass of CX_SY_ARITHMETIC, not of CX_SY_MOVE_CAST_ERROR.
" Holding it as the base class (CX_ROOT) and casting to the wrong subclass dumps.
DATA lo_base TYPE REF TO cx_root.
lo_base = NEW cx_sy_zerodivide( ).
" Cast to a completely unrelated exception class → MOVE_CAST_ERROR
DATA(lo_wrong) = CAST cx_sy_move_cast_error( lo_base ). " <-- MOVE_CAST_ERROR dump
Fix¶
All examples use the same setup as the reproduce example — paste directly into SE38.
REPORT z_move_cast_fix1.
START-OF-SELECTION.
DATA lo_base TYPE REF TO cx_root.
lo_base = NEW cx_sy_zerodivide( ).
" Check the actual runtime type before narrowing — no dump risk
IF lo_base IS INSTANCE OF cx_sy_move_cast_error.
DATA(lo_cast) = CAST cx_sy_move_cast_error( lo_base ).
WRITE: / 'Cast OK:', lo_cast->get_text( ).
ELSE.
DATA(lo_type) = cl_abap_typedescr=>describe_by_object_ref( lo_base ).
WRITE: / 'Not the expected type. Actual:', lo_type->get_relative_name( ).
ENDIF.
REPORT z_move_cast_fix2.
START-OF-SELECTION.
DATA lo_base TYPE REF TO cx_root.
lo_base = NEW cx_sy_zerodivide( ).
TRY.
DATA(lo_cast) = CAST cx_sy_move_cast_error( lo_base ).
WRITE: / 'Cast succeeded:', lo_cast->get_text( ).
CATCH cx_sy_move_cast_error INTO DATA(lx).
WRITE: / 'Cast failed —', lx->get_text( ).
WRITE: / 'Source type: ', lx->source_typename.
WRITE: / 'Target type: ', lx->target_typename.
ENDTRY.
REPORT z_move_cast_fix3.
START-OF-SELECTION.
DATA lo_base TYPE REF TO cx_root.
lo_base = NEW cx_sy_zerodivide( ).
DATA(lo_descr) = cl_abap_typedescr=>describe_by_object_ref( lo_base ).
WRITE: / 'Actual runtime type:', lo_descr->get_relative_name( ).
" Only cast if RTTI confirms the type
IF lo_descr->get_relative_name( ) = 'CX_SY_MOVE_CAST_ERROR'.
DATA(lo_cast) = CAST cx_sy_move_cast_error( lo_base ).
WRITE: / lo_cast->get_text( ).
ELSE.
WRITE: / 'Skipping cast — type mismatch confirmed by RTTI'.
ENDIF.
IS INSTANCE OF is the cleanest guard
IS INSTANCE OF returns true for the exact class AND all its subclasses, matching the same semantics as CAST. It is faster than RTTI and reads clearly.
Prevention¶
- Never use
CASTwithout anIS INSTANCE OFguard unless you are 100% certain of the runtime type (e.g. you just created the object yourself withNEW). - When working with polymorphic factory results, always check the type before narrowing.
See also¶
CX_SY_MOVE_CAST_ERROR— exception classOBJECTS_OBJREF_NOT_ASSIGNED— null reference dump- Transaction ST22 — ABAP runtime error analysis