A CATCH block can be referred to as an end block because it defines end-of-block processing for the block that encloses it. End blocks are always part of another block called the associated block. End blocks must appear in the associated block after the last executable statement and before the END statement. The other type of end block is the FINALLY block that is discussed in later topics.

The CATCH statement defines the start of an end block that only executes if a condition is raised in its associated block, and the type of condition raised is the type specified in the CATCH statement (or a subtype of that type). For example:

DO TRANSACTION ON ERROR UNDO, THROW:

    FIND FIRST Customer WHERE CustNum=1000. 
    RUN CreditCheck.p(Customer.CustNum).

    /* CATCH associated with DO TRANSACTION */ 
    CATCH eAppError AS Progress.Lang.AppError:
        MESSAGE "This customer is on Credit Hold.".
    END CATCH. 
END.

/* CATCH associated with Procedure (Main) block */ 
CATCH eSysError AS Progress.Lang.SysError:
    MESSAGE "Customer record does not exist.".
END CATCH.

In this example:

  • The THROW directive tells the AVM to propagate any unhandled errors to the procedure (main) block, since it is the enclosing block of the DO TRANSACTION block. Notice there is a CATCH block waiting to handle any Progress.Lang.AppError object that may be raised from the RUN statement. If a Progress.Lang.AppError object is raised, the CATCH block handles the error and it is not passed to the procedure block.
  • When running the code, if the FIND statement fails, and there is no error handler present for this error type, it raises a Progress.Lang.SysError. Since Progress.Lang.SysError is not handled, the AVM throws the error up the call stack to the procedure block, due to the UNDO, THROW directive of the TRANSACTION block. The AVM finds a compatible CATCH block on the procedure block and then executes the code in the CATCH block.
  • If you delete the CATCH block on the procedure block and run the example code, the AVM propagates the Progress.Lang.SysError object to the main block as before. Since you no longer have an appropriate error handler in the main block, the AVM now executes the default error handling behavior, which is to display the system error message to the default output device.

The CATCH block executes once for each iteration of its associated block that raises a compatible error. A block can have multiple CATCH blocks, and all must come at the end of the associated block.

There can only be one CATCH block for each specific condition type in a block. A CATCH block also handles objects for its subtypes, so it is possible there can be more than one CATCH block that is compatible with a particular condition. In this case, the AVM executes the first CATCH block it encounters that is compatible. For this reason, CATCH blocks should be arranged from the most specific type to the most general. For example, if you had different error handling code for Progress.Lang.SysError objects and Progress.Lang.SoapFaultError objects, put the CATCH block for SoapFaultError objects first. Otherwise, since SoapFaultError objects are a subtype of SysError, a CATCH block for SysError that appears first would handle the SoapFaultError object.