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.
rephrase simplifies error messages 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.
Some example message rephraseings:
(inc "foo")
rephrase: Expected a number, but was given a stringoriginal: Execution error (ClassCastException)... class java.lang.String cannot be cast to class java.lang.Number...(str let)
rephrase: Syntax error: let is a macro, and cannot be used by itself or passed as an argument to a functionoriginal: Syntax error compiling... Can't take value of a macrobar
rephrase: Syntax error: the name bar is not definedoriginal: Syntax error compiling... Unable to resolve symbol: barfoo/bar
rephrase: Syntax error: The namespace foo is unknown, possibly due to a missing requireoriginal: Syntax error compiling... No such namespace: fooSee :ex-messages in config.edn to get an idea of current rephrasings.
rephrase as REPL Middlewarerephrase is injected into your Clojure REPL.
We'll walk you through a typical setup.
For the purposes of this tutorial, we'll assume you:
deps.edn projectCreate (or edit) a deps.edn with an alias for rephrase:
{:aliases {:rephrase-nrepl-middleware
{:extra-deps {org.corfield/rephrase {:mvn/version "1.0.5"}}
:main-opts ["-m" "nrepl.cmdline"
"--middleware" "[org.corfield.rephrase.nrepl/wrap-rephrase,cider.nrepl/cider-middleware]"]}}}
tip
As a beginner, you don't need to understand the details, but this injects rephrase as nREPL middleware.
From Calva:
Start a Project REPL and Connect.
You can invoke this via the View->Command Palette... menu or via the ctrl+alt+c ctrl+alt+j keyboard shortcut.deps.edn for your project type.:rephrase-nrepl-middleware alias.OK button.Now when your code generates errors you'll get rephrased error messages!
rephrase when Starting a REPL from codeBeginners don't typically start a REPL from source code, but if you do such things, you can inject rephrase via the :caught handler:
(require '[org.corfield.rephrase :as rephrase])
(clojure.main/repl :caught rephrase/repl-caught)
tip
In this case, you'd include rephrase in your deps.edn as a simple aliased dep, i.e.:
{:aliases {:rephrase
{:extra-deps {org.corfield/rephrase {:mvn/version "1.0.5"}}}}}
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.
The rephrase 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.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? These fine people already did:
Sean Corfield & lreadEdit 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 |