Handling errors

How to:

Clojure, like its Lisp ancestors, leans on exceptions for dealing with errors. Here’s how you show what you’re made of when things go south.

Throwing an exception is straightforward:

(throw (Exception. "Oops! Something went wrong."))

Catching an exception, you’ll be doing this a lot:

  ;; risky code
  (/ 1 0)
  (catch ArithmeticException e
    (println "Can't divide by zero!"))
  ;; finally block runs no matter what
    (println "Clean-up code goes here.")))

Sample output for the catch block above:

Can't divide by zero!
Clean-up code goes here.

Using ex-info and ex-data for richer context about exceptions:

  ;; causing a custom exception
  (throw (ex-info "Custom error" {:type :custom-failure}))
  (catch Exception e
    ;; getting the data out of our custom exception
    (println (ex-data e))))

Sample output:

{:type :custom-failure}

Deep Dive

The error handling story in Clojure isn’t radically different from other Lisps or even Java (from which it inherits the try-catch mechanism). It’s pragmatic; using exceptions is the main path, just like Java, but Clojure offers a functional flavor with ex-info and ex-data for richer error data.

Alternatives for error handling in Clojure include using monadic constructs, such as the either monad from libraries like cats, or core.async for channel-based error propagation. However, these are more complex and used in specific scenarios.

Historically, error handling in programming languages has evolved from simple status returns to the more sophisticated exception handling mechanisms of modern languages. Clojure opts for simplicity and a touch of functional programming, blending the old and the new.

See Also