The error handling facilities provided by the Common Lisp and LispWorks-specific Condition System are available in runtimes delivered at level 0.
If you deliver at higher levels, then be aware that the full Condition System will not be present by default in your application. If you choose not to retain the full Condition System, you can make use of the more limited, but smaller, error systems available with Delivery. It is useful to make the application handle errors appropriately, because it is generally used by non-Lisp programmers, and it does not have the IDE so it is less easy to debug.
Simplified error handling is still possible in applications without the Condition System. They can only trap "conditions" of type ERROR
or WARNING
. If an application signals any condition other than WARNING
or SIMPLE-WARNING
, the condition is categorized as one of type ERROR
, and therefore can be trapped.
There are two classes of error an application is likely to need to handle: errors generated by the application, and errors generated by the Lisp system.
Error conditions that can occur in your application domain can be handled easily enough if you define your own error handling or validation functions to trap them. For instance, you might ordinarily have the following code, which manages an error condition and makes a call to error
:
.....
(let ((res (call-something)))
(when res
(generate-error res))
.....
(defun generate-error(res)
(error 'application-error
:error-number res))
You can easily define a version of generate-error
that does all the work:
(defun generate-error (res)
(let ((action
(capi:prompt-with-list
'(("Abort Operation" . abort)
("Retry Operation" . retry)
("Ignore Error")
("Quit" . stop-application)
("Do Something Else" . do-something-else))
(find-error-string res)
:print-function 'first
:value-function 'rest)))
(case action
((abort retry) (invoke-restart action))
((nil))
(t (funcall action)))))
Errors generated by the Lisp system, rather than the application domain, are a little harder to deal with.
Suppose your application performs an operation upon a file. The application calls a system function to complete this operation, so when there is no error system, any errors it generates must be caught by the application itself.
The best solution to this problem is to wrap an abort
restart around the operation. For example:
(defun load-knowledge-base (name pathname)
(restart-case
(internal-load-knowledge-base name pathname)
(abort ()
(capi:display-message
"Failed to load knowledge base ~a from file ~a"
name (namestring pathname))
nil)))
Another solution would be to use a handler, as in the example below:
(defun my-handler (type &rest args)
(if (symbolp type)
(apply 'capi:display-message
"An error of type ~A occurred, args ~A"
type args)
(apply 'capi:display-message args)))
The disadvantage of this approach is that the message is unclear.
In general, the application should not cause Lisp errors. Because it is difficult to ensure that these never happen, it is a good idea for the application to wrap an error handler around all its code. For example:
(handler-bind ((error 'application-handler-error))
(loop
(catch 'application-error
(setup-various-things)
(do-various-things))))
(defun application-handler-error (condition)
(when *application-catch-errors*
(progn (give-some-indication-of-error)
(do-some-cleanup)
(throw 'application-error nil))))
(when *application-catch-errors*
is nil
, this just returns and then the debugger is invoked).
In addition, the areas that are more prone to errors should be dealt with specifically. For example, file access is prone to error, so it should wrapped with error handling.
The variable cl:*debugger-hook*
can be used to handle errors that are not caught by other handlers.
See Error handling in delivered applications for more information about using this in a delivered application.
Condition types are classes like any other class, so may be shaken out. However the code may contain many references to condition types through error calls that are never going to happen in the application. Therefore, there is a special deletion action for conditions, which is controlled by the deliver
keywords :condition-deletion-action, :keep-conditions and :packages-to-remove-conditions.
When a condition is deleted (that is when :condition-deletion-action
is :delete
), trying to signal it returns a simple-error
, which means that it got the wrong type. On the other hand, it has all the information in the format-arguments
slot. If the conditions are redirected (that is, when :condition-deletion-action is :redirect
), a stricter type is returned, but some of the information may be lost, because the condition that it redirects to has fewer slots.
User defined conditions are kept, unless:
:none
, in which case all the conditions are eliminated, or :minimal
, in which case all the user conditions are deleted.LispWorks Delivery User Guide - 15 Feb 2015