Next: , Previous: , Up: Errors—Unintentional Nonlocal Exits   [Index]


F.6.2.3 Using condition-case to Trap and Handle Errors with Handlers

Error Handling Concepts

condition-case

You can arrange to traperrors’ occurring in a part of your program by establishing an ‘error handler’, with the special form condition-case.

Function: condition-case var protected-form handlers…

Each error that occurs has an error symbol that describes what kind of error it is, and which describes also a list of condition names. Emacs searches all the active condition-case forms for a handler that specifies one or more of these condition names; the innermost matching condition-case handles the error. Within this condition-case, the first applicable handler handles the error. After executing the body of the handler, the condition-case returns normally, using the value of the last form in the handler body as the overall value.

PROTECTED-FORM

This special form establishes the error handlersHANDLERS’ around the execution of ‘PROTECTED-FORM’. If ‘PROTECTED-FORM’ executes without error, the value it returns becomes the value of the condition-case form; in this case, the condition-case has no effect.

HANDLERS

Each of the ‘HANDLER’ s is a list of the form (conditions body…).

CONDITIONS’ is an error condition name to be handled, or a list of condition names (which can include debug to allow the debugger to run before the handler). A condition name of ‘t’ matches any condition.

BODY’ is one or more Lisp expressions to be executed when this handler handles an error.

VAR

The argument ‘VAR’ is a variable. condition-case does not bind this variable when executing the protected-form, only when it handles an error. At that time, it binds ‘VAR’ locally to an error description, which is a list giving the particulars of the error. The error description has the form ‘(error-symbol . data)’. The handler can refer to this list to decide what to do. If ‘VAR’ is ‘nil’, that means no variable is bound. Then the error symbol and associated data are not available to the handler.

Sometimes it is necessary to re-throw a signal caught by condition-case, for some outer-level handler to catch. Here’s how to do that:

(signal (car err) (cdr err))

where ‘ERR’ is the error description variable.

  1. Condition-Case Examples
    (condition-case nil
        (delete-file filename)
      (error nil))
    

    Listing F.3: Simple condition-case Example

    This catches any ‘error’ on file deletion and returns ‘nil’. You can also use the macro ignore-errors in a simple case like this.

    • The condition-case construct is often used to traperrors’ that are predictable, such as failure to open a file in a call to insert-file-contents.
    • It is also used to traperrors’ that are totally unpredictable, such as when the program evaluates an expression read from the user.
    • The second argument of condition-case is called the protected form. The error handlers go into effect when this form begins execution and are deactivated when this form returns. They remain in effect for all the intervening time. In particular, they are in effect during the execution of functions called by this form, in their subroutines, and so on.
    • Strictly speaking, ‘errors’ can be signaled only by Lisp primitives (including signal and error) called by the protected form.
    • The arguments after the protected form are handlers. Each handler lists one or more condition names (which are symbols) to specify which errors it will handle. The ‘error symbol’ specified when an ‘error’ is signaled also defines a list of condition names. A handler applies to an ‘error’ if they have any condition names in common.
    • The search for an applicable handler checks all the established handlers starting with the most recently established one.
    • If you want to be able to debug ‘errors’ that are caught by a condition-case, set the variable debug-on-signal to a non-‘nil’ value.
    • You can also specify that a particular handler should let the debugger run first, by writing ‘debug’ among the conditions, like this:
      (condition-case nil
          (delete-file filename)
        ((debug error) nil))
      

      Listing F.4: Run Debugger First on File Deletion

      The effect of ‘debug’ here is only to prevent condition-case from suppressing the call to the debugger. Any given ‘error’ will invoke the debugger only if debug-on-error and the other usual filtering mechanisms say it should.

    Example Error Handlers

    (error nil)
    
    (arith-error (message "Division by zero"))
    
    ((arith-error file-error)
     (message
      "Either division by zero or failure to open a file"))
    

    Handling a Specific Error

    Here is an example of using condition-case to handle the ‘error’ that results from dividing by zero. The handler displays the error message (but without a beep), then returns a very large number. The handler specifies condition namearith-error’ so that it will handle only ‘division-by-zero’ errors. Other kinds of errors will not be handled (by this condition-case).

    (defun safe-divide (dividend divisor)
      (condition-case err
          ;; Protected form.
          (/ dividend divisor)
        ;; The handler.
        (arith-error                        ; Condition.
         ;; Display the usual message for this error.
         (message "%s" (error-message-string err))
         1000000)))
    ⇒ safe-divide
    
    (safe-divide 5 0)
         -| Arithmetic error: (arith-error)
    ⇒ 1000000
    
    (safe-divide nil 3)
         error→ Wrong type argument: number-or-marker-p, nil
    

    Listing F.5: Handling a Specific Error

    Handling All Kinds of Errors

    Handling All Errors

    Here is a condition-case that catches all kinds of errors, including those from error:

    (setq baz 34)
         ⇒ 34
    
    (condition-case err
        (if (eq baz 35)
            t
          ;; This is a call to the function error.
          (error "Rats!  The variable %s was %s, not 35" 'baz baz))
      ;; This is the handler; it is not a form.
      (error (princ (format "The error was: %s" err))
             2))
    -| The error was: (error "Rats!  The variable baz was 34, not 35")
    ⇒ 2
    

    Listing F.6: Handling All Errors

  2. Additional Error Handling Concepts

    condition-case-unless-debug Macro

    macro: condition-case-unless-debug var protected-form handlers…

    The macro condition-case-unless-debug provides another way to handle debugging of such forms. It behaves exactly like condition-case, unless the variable debug-on-error is non-‘nil’, in which case it does not handle any errors at all.

    Once Emacs decides that a certain handler handles the ‘error’, it returns control to that handler.

    • Emacs unbinds all variable bindings made by binding constructs that are being exited,
    • and executes the cleanups of all unwind-protect forms that are being exited.
    • Once control arrives at the handler, the body of the handler executes normally.
    • After execution of the handler body, execution returns from the condition-case form. All it can do is clean up and proceed.

    error-message-string Function

    Function: error-message-string error-descriptor

    This function returns the error message string for a given error descriptor. It is useful if you want to handle an error by printing the usual error message for that error.

    Resemblance to Catch and Throw

    Error signaling and handling have some resemblance to throw and catch but they are entirely separate facilities: An ‘error’ cannot be caught by a catch, and a throw cannot be handled by an error handler.

    • using throw when there is no suitable catch signals an ‘error’ that can be handled.

Next: Ignoring Errors, Previous: Error Processing—Error Debugging, Up: Errors—Unintentional Nonlocal Exits   [Index]