Beyond Try-Catch: Common Lisp’s Restart System
Popular programming languages such as C++, Java and Python include one mechanism for error handling: throw an exception and hope someone above in the call stack knows what to do. This is no doubt a powerful mechanism, but Common Lisp, decades ago, built something far more powerful in its place.
The Condition/Restart system is, in my view, the single most under-appreciated feature in all of programming language design. It separates the detection of a problem from the policy for resolving it, without unwinding the call stack.
The Restart system has three distinct elements:
- Conditions are objects that describe a situation — not necessarily an error. A condition might signal “this log entry has a malformed date” without implying that the program should crash.
- Restarts are concrete recovery strategies, established by the code that knows how to recover. They say: “If something goes wrong in here, here are the ways I can deal with it.”
- Handlers define the policy layer. They’re established by the code that knows what the right decision is — typically higher up the call stack. Crucially, handlers run without unwinding the stack. The context where the condition was signaled is still alive.
Here’s the key insight: the code that detects a problem often knows how to recover in several different ways, but doesn’t know which recovery strategy is appropriate. The calling code knows the policy, but doesn’t know the low-level recovery mechanics. The condition system lets each side do what it is good at.
Let us go through a simple example to understand how the whole thing fits in. In the following, “process-number” is the lowest level function that processes a number. It knows how to handle positive numbers, but if it encounters a negative number, it triggers an “error” condition. Interestingly, it knows how to recover, but cannot decide on its own which recovery to use.

In the above code fragment, “negative-number” is the condition that will be triggered.
Next, we have “process-list”, a higher level function that calls “process-number” for each element in its collection.

Finally, at the top level we have two functions, each of which sets its own recovery policy before invoking “process-list”.

Let us see what happens when we invoke these functions interactively, in the IDE.

What will happen if we invoke “process-list”, which does not define any restart policy?

Here is where Common Lisp excels. If no handler is established and a condition triggers the debugger, the user sees the available restarts and can choose one interactively. What is interesting is that we are able to select two different policies at the two different error points!
I hope this article shows the elegance and power of Common Lisp in the context of error handling. Once you understand this example, you will be able to appreciate complex real-world use cases.
You can download the source code <here>.
Have a great weekend!