Rephrase exceptions.
Error messages in Clojure have been a long-standing source of frustration for beginners. This library is an experiment in rephrasing exceptions to make them more beginner-friendly.
The library provides two main functions:
org.corfield.rephrase/repl-caught - a replacement for clojure.main/repl-caught that rephrases exceptions before printing them (via the :caught option when starting a REPL),org.corfield.rephrase.nrepl/wrap-rephrase - nREPL middleware that applies repl-caught to produce rephrased exceptions in nREPL sessions.There is also a helper function that applications or tools might use:
org.corfield.rephrase/rephrase-err->msg - a replacement for clojure.main/err->msg that takes an exception and returns a rephrased error message string.Error messages are rephrased to a single line, with the cause first, followed by the location of the error. This makes inline display of error messages in editors easier to read, especially if the editor normally suppresses the second line of the standard exception report (e.g., Calva), or truncates long messages.
Add the following dependency to your project (or as a global dependency
in your user-level deps.edn or profiles.clj file):
org.corfield/rephrase {:mvn/version "1.0.0"}
This is probably the most common way to use this library.
For use with nREPL, add org.corfield.rephrase.nrepl/wrap-rephrase to your
list of middleware. See
nREPL Middleware Setup
in the CIDER documentation for details on how to do this.
:caughtYou can also use org.corfield.rephrase/repl-caught directly when starting a REPL, by passing it as the value of the :caught option. For example:
(require '[org.corfield.rephrase :as rephrase])
(clojure.main/repl :caught rephrase/repl-caught)
You can add more mappings by adding org/corfield/rephrase-user.edn to your
classpath with the same structure as config.edn.
See the source
for details.
Configuration is available under four keys in the EDN file:
:ex-types - a hash map from exception class names (as symbols) to friendly names (as strings); this is used to rephrase the exception type itself in the error message.:inline-types - a vector of pairs: each pair is typically a class name (as a regex string) and a friendly name (as a replacement string); this is used to replace occurrences of the class name in the exception message with the friendly name.:removals - a vector of regex strings; any occurrence of these strings in the exception message will be removed.:ex-messages - a vector of pairs (but see below): each pair is a regex string and a replacement string; this is used to rephrase specific messages to more beginner-friendly versions.The :ex-types mapping is applied to the exception type, independently.
The exception message is rephrased by mapping the :inline-types first,
then applying the :removals, and finally applying the :ex-messages
replacements. All three of these are applied in the order they are defined
in the configuration files (default first, then any user mappings), so more
specific mappings should come first, then more general ones. All mappings
are applied -- rephrasing does not stop after the first match.
The pairs in :ex-messages may have an optional third element, a symbol, that
indicates the mapping should only be applied to messages of a specific exception
type. If the symbol is present, the mapping will only be applied if the
original exception type matches the symbol (i.e., before rephrasing via :ex-types).
This allows for more specific rephrasings that only apply to certain exception types, while still allowing more general rephrasings to apply to all messages.
There have been a lot of discussions and libraries started around this topic. I've toyed with the idea of rephrasing exceptions for several years, and have started to write a library like this more than once.
Adrian Smith provided a long list of links on Slack that has been great background reading for this project.
Much of the initial mapping of class names and rephrasing of exception messages comes from Babel.
Copyright © 2026 Sean Corfield
Distributed under the Eclipse Public License 2.0
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |