Try OpenEdge Now
skip to main content
ABL Reference
ABL Syntax Reference : CATCH statement
 

CATCH statement

Defines an ERROR or STOP condition-handling end block for any undoable ABL block. An end block is an ABL block that can occur only within and at the end of another block. The block containing the end block is known as the associated block. End blocks must occur between the last line of executable code in the associated block and its END statement.
A CATCH block executes when an ERROR or STOP condition raised in the associated block is compatible with the error or stop type that is specified in the CATCH statement. To be compatible, the error or stop type must be the type specified in the CATCH statement for an object that is thrown in the associated block, or it must be a sub-type (sub-class) of the specified type that is thrown. The CATCH block thus executes when a thrown object of the specified error or stop type is caught by the CATCH statement, at which point the object can then be processed (handled) in the CATCH block.
For ERROR condition-handling, CATCH blocks for error types (error CATCH blocks) take precedence over any implicit or explicit ON ERROR directives specified for the associated block. Similarly for STOP condition-handling, CATCH blocks for stop types (stop CATCH blocks) take precedence over any implicit or explicit ON STOP directives specified for the associated block.
This is the syntax for the CATCH statement and its related blocks:

Syntax

block-statements
    CATCH error-or-stop-variable AS [ CLASS ]error-or-stop-class-type:
        catch-logic
    END [ CATCH ] .
[block-end-statement]
block-statements
All of the statements of an enclosing associated ABL block, except for its block-end-statement. The enclosing associated block can be any ABL block, including another CATCH block.
error-or-stop-variable
The variable name that references the error object generated by the error condition. Typically, you do not define the error-or-stop-variable ahead of time with the DEFINE VARIABLE statement. The AVM recognizes a new variable name on the CATCH statement as a new error-or-stop-variable definition. Each CATCH in an associated block must have a unique error-or-stop-variable. You can reuse an error-or-stop-variable name in a different associated block, as long as its type is compatible with the new definition.
[ CLASS ]error-or-stop-class-type
A compatible class type name for a given error or stop type where the object:
*Implements Progress.Lang.Error, for example:
*Progress.Lang.SysError or a built-in subclass (system errors)
*Progress.Lang.AppError or a user-defined subclass (application errors)
*System.Exception or a .NET subclass (.NET Exceptions)
*Progress.Lang.StopError (STOP conditions raised for certain system errors)
*Is Progress.Lang.Stop or a built-in subclass (STOP conditions raised for supported ABL features, such as a STOP-AFTER time-out on a DO, FOR, or REPEAT statement)
Note: Therefore, if you specify Progress.Lang.SysError, this one block can handle all ERROR conditions raised from a system error and all STOP conditions raised from a system error. If you specify Progress.Lang.Error, this one block can handle all ERROR conditions (including system errors, application errors, and .NET Exceptions) and all STOP conditions raised from a system error. If you specify Progress.Lang.Stop, this one block can handle all STOP conditions raised from supported ABL features.
Optionally, you can provide the CLASS keyword.
catch-logic
All statements allowed in a CATCH block, which can include any valid ABL statement. For more information on CATCH block execution, see the Notes of this reference entry.
block-end-statement
For all associated ABL blocks except a main external procedure block, the END statement terminates and encloses the associated block of the CATCH block. External procedure blocks have no terminating END statement.

Examples

The following code fragment shows CATCH blocks for associated DO blocks:
DO ON ERROR UNDO, LEAVE:
  FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 5000.

  CATCH oneError AS Progress.Lang.SysError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.

  CATCH twoError AS Progress.Lang.ProError:
    MESSAGE twoError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
END. /* FIRST DO */

DO ON ERROR UNDO, LEAVE:
  FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 6000.

  /* You can reuse an error-or-stop-variable from a different
     associated block */
  CATCH oneError AS Progress.Lang.SysError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.

  /* NOT LEGAL: Each CATCH block in an associated block must have a unique
     error-or-stop-variable. */
  CATCH oneError AS Progress.Lang.ProError:
    MESSAGE oneError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
  END CATCH.
END. /* SECOND DO */
In the following example, the CATCH block will catch any ABL system error or a STOP condition raised by an unexpected system error:
DEFINE VARIABLE iCust AS INTEGER NO-UNDO INITIAL 5000.

FIND Customer NO-LOCK WHERE Customer.CustNum = iCust. /* Will fail */

/* Won't execute because FIND fails */
MESSAGE "Customer found" VIEW-AS ALERT-BOX BUTTONS OK.

/* The associated block for this CATCH block is the main block of the .p */
CATCH eSysError AS Progress.Lang.SysError:
  MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.

Notes

*In order to enable the AVM to throw and catch Progress.Lang.Stop (and subclass) or Progress.Lang.StopError objects, you must specify the Catch STOP (-catchStop) startup parameter with the value 1 (-catchStop 1). Otherwise, the AVM raises the STOP condition only, without throwing the associated object.
*Stop object support is available as a Technology Preview only in OpenEdge Release 11.7.
*Class-based error or stop objects can be thrown from an OpenEdge application server and handled by a CATCH block on an ABL client. To be throwable from an OpenEdge application server to an ABL client, any user-defined error classes must be defined on both the server and client sides, and the classes must be defined as SERIALIZABLE. Otherwise, ERROR and STOP conditions from the server are handled on the client as provided for handling any ERROR and STOP. In addition, note that any STOP condition messages are written to the server log file, even if a stop object with the messages is caught on the client. For the full list of restrictions on class-based objects that are passed between server and client, see the Parameter passing syntax entry. For more information on error and stop handling in general, see OpenEdge Development: Error Handling.
*Class-based error and stop objects can also be thrown from an OpenEdge application server to a client for an asynchronous request. In this case, an error object or Progress.Lang.StopError stop object is returned to the client and its reference provided as the value of the ERROR-OBJECT attribute of the asynchronous request object handle. Any other stop object (a Progress.Lang.Stop or a subclass) is returned to the client and its reference provided as the value of the STOP-OBJECT attribute of the asynchronous request object handle.
*One or more CATCH blocks are positioned at the end of the associated block. If a FINALLY end block is also used, the CATCH blocks come before the FINALLY block. The syntax for an associated block using end blocks is as follows:
associated-block:
  .
  .
  .
  [ CATCH options :
    .
    .
    .
  END [ CATCH ] . ] ...
  [ FINALLY :
    .
    .
    .
  END [ FINALLY ] . ]
END. /* associated-block */
Note that a CATCH block can also contain a CATCH or FINALLY block, just as a FINALLY block can contain a CATCH or FINALLY block. Note also that if the associated block does not handle a raised STOP condition with an ON STOP phrase or compatible CATCH block, or a raised QUIT condition with an ON QUIT phrase, the FINALLY block does not run. For more information on FINALLY blocks, see the FINALLY statement reference entry.
Thus, the following blocks can have a CATCH block:
*CATCH block (recursive)
*DO (if configured with TRANSACTION, an ON ERROR UNDO directive, or an ON STOP UNDO directive)
*FOR
*REPEAT
*Main (.p procedure file)
*Internal procedures
*User-defined functions
*Methods
*Property accessors
*Constructors
*Destructors
*ON
*CATCH
*FINALLY
Note: All these blocks, except DO, have implicit error and stop handling directives.
*ABL issues a compile-time error if a CATCH block is present in a simple DO block, since simple DO blocks do not have implicit error and stop handling capabilities. DO blocks must have an explicit TRANSACTION, ON ERROR UNDO directive, or ON STOP directive coded in the DO statement in order to have a CATCH block. For example:
DO TRANSACTION:
  . . .
  CATCH . . . :
    . . .
  END CATCH.
END.
*The code within a CATCH block is only executed if a compatible ERROR or STOP condition is raised that throws an object of type error-or-stop-class-type within the body of the associated block. This behavior is also true if any subroutine called by the associated block returns or raises an error of type error-or-stop-class-type. When ERROR or STOP is raised, if there is an active transaction, the associated block is undone before the AVM begins executing the statements within the CATCH block.
*An associated block may have multiple CATCH blocks, each of which handles a different error-or-stop-class-type. If a thrown error-or-stop-class-type satisfies multiple CATCH statements, the AVM executes the code in the first CATCH block that matches the error-or-stop-class-type. It never executes multiple CATCH blocks for the same thrown error or stop object. Therefore, if multiple CATCH blocks are specified for compatible class types, the CATCH block for the more specialized error or stop classes should come first, as shown:
FOR EACH Customer:
  /* Code body of the associated block */

  /* This CATCH specifies the most specialized user-defined error class.
     It will catch only myAppError error objects or objects derived from
     myAppError. */
CATCH eMyAppError AS Acme.Error.myAppError:
    /*Handler code for Acme.Error.myAppError condition. */
  END CATCH.

  /* This CATCH will handle Progress.Lang.AppError or any user-defined
     application error type, except for eMyAppError which would be
     handled by the preceding CATCH block. */
  CATCH eAppError AS Progress.Lang.AppError:
    /* Handler code for AppError condition. */
  END CATCH.

  /* This CATCH will handle any error raised by an ABL statement. Since
     it inherits from the same object as AppError in the class hierarchy,
     this CATCH could come before or after the CATCH for AppError */
CATCH eSysError AS Progress.Lang.SysError:
/* Handler code for SysError condition. */
END CATCH.

    /* This will catch any possible error raised in the ABL. */
  CATCH eError AS Progress.Lang.Error:
    /* Handler code for any error condition. */
  END CATCH.
END. /* FOR EACH Customer, associate block */
*The compiler will issue a warning message if a block contains a CATCH block that is not reachable. For example, the following code will cause the compiler to issue a warning, since the CATCH of myAppError can never be executed:
FOR EACH Customer:
  /* Code body of the associated block. */

  /*This will catch all application errors */
  CATCH eAppError AS Progress.Lang.AppError:
    /* Handler code for AppError condition */
  END CATCH.

  /* Never get here, because myAppError is a subtype of
     Progress.Lang.AppError */
  CATCH eMyAppError AS Acme.Error.myAppError:
    /* Handler code for myAppError condition */
  END CATCH.
END. /* FOR EACH Customer, Associated Block */
*It is valid to have both an explicit ON ERROR UNDO or ON STOP UNDO directive and a CATCH block on the same associated block. In that case, the CATCH block takes precedence for any compatible ERROR or STOP condition. For example, if an ERROR condition is raised in a block that has both a compatible CATCH block and an ON ERROR phrase, the CATCH block catches and handles the error object. However, if the ERROR condition is not handled by a CATCH block, it is then handled by the ON ERROR UNDO directive of the associated block. Again, this can be either an explicit ON ERROR phrase or the implicit (default) ON ERROR UNDO directive for the particular block type. The is also true for the handling of STOP conditions raised in an associated block by its CATCH blocks and any ON STOP UNDO directive coded for the block.
Therefore, you might want the CATCH block to catch certain error or stop object types to handle directly, and have all other ERROR or STOP conditions handled by the ON ERROR UNDO or ON STOP UNDO directive of the associated block. Note also that with an ON ERROR or ON STOP phrase, the CATCH block does not have to catch a corresponding error or stop object type. That is, you can have an ON ERROR UNDO directive and a CATCH block that catches a stop object.
*A raised STOP condition and its stop object is thrown up the call stack until it is handled either by a compatible CATCH block or an ON STOP phrase somewhere on the stack, which ever one is closest to the point where the stop object is initially thrown. If not handled, the STOP condition is raised to the top of the stack and any messages are displayed to the output device.
*You can code a CATCH block within a containing (associated) CATCH block. In this case, the contained CATCH block only handles ERROR or STOP conditions raised within the associated CATCH block. However to prevent infinite looping, any UNDO, THROW statement within the top-level CATCH block or any CATCH block nested within it immediately throws its error or stop object to the block that contains the associated block of the top-level CATCH block. For example:
FOR EACH Customer:
  /* FOR EACH code body */

  DO ON ERROR UNDO, LEAVE: /* Associated block of top-level CATCH */
    /* DO code body */
    CATCH eAppError AS Progress.Lang.AppError:
      /* CATCH eAppError code body raises a system error */

CATCH eSysError AS Progress.Lang.SysError:
UNDO, THROW eSysError. /* Will be handled by CATCH anyError... */
END CATCH.
    END CATCH.
  END. /* DO */

  CATCH anyError AS Progress.Lang.Error:
    /* Handler code for anyError condition */
  END CATCH.
END. /* FOR EACH Customer */
*The CATCH block is an end block of its associated block. A CATCH block can only execute after the execution (or each iteration) of the associated block. The CATCH block only executes when ERROR or STOP is raised in the associated block and the CATCH error or stop class type is compatible with the type of raised ERROR or STOP. Any transaction within the associated block will be undone and records will be released before the CATCH block executes.
*The CATCH block is an undoable block with implicit ON ERROR UNDO, THROW error handling, and like all blocks, CATCH blocks implicitly throw all stop objects. You cannot explicitly override the implicit ON ERROR UNDO directive for a CATCH block. If a statement within the CATCH block raises ERROR and there is no nested CATCH block, the CATCH block will be undone, and the ERROR will be raised in the block that encloses the associated block of the CATCH block. Error is not raised in the associated block. Otherwise, infinite looping could occur. The same is true if the CATCH block raises STOP.
Thus, a statement that raises ERROR or STOP within a CATCH block causes the following to occur:
1. UNDO the CATCH block.
2. Leave the CATCH block and its associated block.
3. THROW the error or stop object to the block enclosing its associated block. If the CATCH block is at the routine level, the error or stop object gets thrown to the caller of the routine.
The same behavior occurs for an explicit UNDO, THROW statement in a CATCH block. For example:
DO ON ERROR UNDO, LEAVE:
  /* Check for Orders */

  /* Fails and throws Progress.Lang.SysError. Execution goes to CATCH */
  FIND FIRST Order NO-LOCK WHERE Order.CustNum = 1000.
  MESSAGE "Order found". /* MESSAGE does not execute */

  CATCH eSysError AS Progress.Lang.SysError:
    /* Check if Customer exists, which fails. ON ERROR UNDO, THROW for
       CATCH will raise ERROR in main block of .p - execution goes to
       CATCH in main block */*/
    FIND FIRST Customer NO-LOCK WHERE Customer.CustNum = 1000.
  END CATCH.
END.

MESSAGE "Customer found". /* MESSAGE does not execute */

/* This CATCH is for the main block of the .p */
CATCH eSysError AS Progress.Lang.SysError:
  MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX BUTTONS OK.
END CATCH.
*If there is a FINALLY block in the associated block, the FINALLY code is executed before ERROR or STOP gets raised in the block enclosing the associated block.
*The scope of buffers referenced in a CATCH block is the smallest enclosing block outside of the associated block that encompasses all references to the buffer.
In this example, DO TRANSACTION and CATCH both reference the Customer buffer:
/* myproc.p */
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.

MESSAGE myInt AVAILABLE(Customer).     /* will see '5 no'*/

DO TRANSACTION ON ERROR UNDO, LEAVE:
  myInt = 10.
  FIND FIRST Customer NO-LOCK.
  MESSAGE myInt AVAILABLE(Customer).   /* will see '10 yes' */

/* Returns ERROR (throws Progress.Lang.AppError). The block is undone and
  the Customer buffer is released - execution goes to CATCH. */
  RUN Test.p (Customer.CustNum).
  ...

  CATCH eAppError AS Progress.Lang.AppError:
    MESSAGE myInt AVAILABLE(Customer). /* will see '5 no'*/
  END CATCH.
END.
As the result of the reference to the Customer buffer in the CATCH block in the previous example, the scope of the Customer buffer is raised to the procedure level (myproc.p), since the smallest enclosing block of the DO TRANSACTION is the procedure block.
*The code in any CATCH block can contain an explicit flow-of-control directive, meaning LEAVE, NEXT, RETRY, RETURN, or THROW. (RETRY and THROW require UNDO.) Since CATCH is an undoable block, LEAVE, NEXT, and RETRY without a label will apply to the CATCH block itself and not the associated block.
If you want LEAVE, NEXT, or RETRY to apply to the associated block of a CATCH block, you must use the existing label syntax for these statements.
An explicit UNDO, THROW in a CATCH block causes the AVM to raise ERROR or STOP in the block that encloses the associated block of the CATCH block; not the associated block itself.
In this example, there is a LEAVE of the FOR EACH bock blk1 for any occurrence of a PrinterDown application error and a NEXT to iterate the same FOR EACH block with the next Customer for any other application printer error:
DEFINE VARIABLE iOrdNum AS INTEGER NO-UNDO.
DEFINE VARIABLE iTries  AS INTEGER NO-UNDO INITIAL 1.

blk1:
FOR EACH Customer NO-LOCK:
  UPDATE iOrdNum.

  FIND Order NO-LOCK WHERE Order.CustNum = Customer.CustNum
    AND Order.OrderNum = iOrdNum.

  /* Successfully found Order. Try to print invoice. If PrintInvoice.p
     throws an Acme.Error.PrinterDownError error, just leave the FOR EACH
     block. If PrintInvoice.p throws any other type of AppError, try with
     the next customer. */
  RUN PrintInvoice.p (INPUT Order.OrderNum).
  ...

  CATCH pde AS Acme.Error.PrinterDownError:
    MESSAGE "Printer down...aborting".
    /* Leave the FOR EACH. */
    LEAVE blk1.
  END CATCH.

  CATCH eAppError AS Progress.Lang.AppError:
    MESSAGE "Problem printing invoice for order " iOrdNum.
    /* Execution resumes with the next iteration of the FOR EACH */
    NEXT blk1
  END CATCH.
END. /* FOR EACH Customer */
If there is no explicit flow-of-control statement in the CATCH block, the AVM leaves the CATCH block and executes the default ERROR or STOP action for the associated block after executing the last statement in the CATCH block and any code within a FINALLY block. This means RETRY for all blocks. When no input-blocking statements are present and the RETRY function is not used in the block, the AVM prevents infinite looping by changing the RETRY to NEXT for iterating blocks and to LEAVE for non-iterating blocks.
Note: For more information on infinite loop protection in error handling, see OpenEdge Getting Started: ABL Essentials.
In the following code, if an Acme.Error.myAppError is caught, the explicit UNDO, THROW statement causes the caught error to be thrown to the block enclosing the FOR EACH (remember that UNDO, THROW in a CATCH means leave associated block, then throw). However, if a Progress.Lang.SysError is caught the AVM will execute a NEXT on the FOR EACH block. Thus:
FOR EACH Customer ON ERROR UNDO, LEAVE:
  /* FOR EACH code */

  CATCH eSysError AS Progress.Lang.SysError:
    /* Handler code for SysError condition */

    /* RETRY on FOR EACH after leaving the CATCH, which becomes NEXT if there are
       no I/O statements and the RETRY function is not used in the block.*/
  END CATCH.

  CATCH myAppErr AS Acme.Error.myAppError:
    /* Handler code for myAppError condition */

    /* THROW error to block enclosing the FOR EACH */
    UNDO, THROW myAppErr.
  END CATCH.
END.
*The presence of a CATCH in an undoable block causes the AVM to suppress Progress system error messages for all statements within the block, in the same way that NO-ERROR works on individual statements. If there is a CATCH on Progress.Lang.SysError, the message(s) will be added to the Progress.Lang.SysError object that is available in the CATCH. If there is no CATCH on Progress.Lang.SysError, and the Progress.Lang.SysError is not re-thrown (by way of ON ERROR UNDO, THROW on the block), the error messages from the Progress.Lang.SysError will be written to the current output destination and the AVM will execute the ON ERROR directive for the block.
Similar processing happens for a STOP condition raised because of a system error where a Progress.Lang.StopError object is thrown. Note that on an OpenEdge application server, the error messages added to the thrown Progress.Lang.StopError object are also written to the application server log file even though they are available to the client in the serialized object.
In this example, a CATCH handles the error and the error message is suppressed:
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.

DO ON ERROR UNDO, LEAVE:
  /* Raises ERROR and throws Progress.Lang.SysError. Error message
     suppressed and execution goes to CATCH */
  FIND Customer 1000.
  MESSAGE "After Find".  /* Will not get here */

  CATCH eSysError AS Progress.Lang.SysError:
    /* Will display "** Customer record not on file. (138)" */
    MESSAGE eSysError:GetMessage(1) VIEW-AS ALERT-BOX.
    /* Leave the CATCH, then the DO block */
  END CATCH.
END.
In this example, there is no CATCH block that handles the error and the error message is not suppressed:
DEFINE VARIABLE myInt as INTEGER NO-UNDO INITIAL 5.

DO ON ERROR UNDO, LEAVE:
  /* Raises ERROR and displays "Customer record not on file. (138)"
     UNDO, LEAVE the block */
  FIND Customer 1000.
  MESSAGE "After Find". /* Won't get here */

  CATCH ae AS Progress.Lang.AppError:
    MESSAGE ae:GetMessage(1) VIEW-AS ALERT-BOX.
  END CATCH.
END.
*Any error in the context of a WAIT-FOR statement (ABL or .NET) is raised as a STOP condition. This is also true if a .NET error terminates the WAIT-FOR statement. In this case, you need to be aware that this STOP condition is in fact a .NET Exception represented by an error object that implements the Progress.Lang.Error interface, though it is not a Progress.Lang.SysError or Progress.Lang.StopError. Therefore, to catch all conditions, including these .NET Exceptions, you must catch Progress.Lang.Error.
*Almost all STOP conditions are trappable with a stop CATCH block or the ON STOP phrase. In some cases, the AVM might ignore STOP conditions at certain levels of the call stack. For example, if the AVM executes a procedure that relies on a lost database connection, the AVM raises the STOP condition and unwinds the call stack until it gets to a level above all references to the lost database. If the AVM encounters a compatible stop CATCH block or the ON STOP phrase before this point, the AVM ignores the CATCH block or phrase. If the AVM encounters a compatible stop CATCH block or the ON STOP phrase after this point, the AVM executes the CATCH block or the directive specified in the phrase, with the CATCH block taking precedence if the ON STOP phrase is on the same associated block.

See also

BLOCK-LEVEL ON ERROR UNDO, THROWstatement, ON ERROR phrase, ON STOP phrase, Progress.Lang.Error interface, Progress.Lang.Stop class, RETURN statement, ROUTINE-LEVEL ON ERROR UNDO, THROWstatement, UNDO statement