clojure/clojure · error · RuntimeException
First argument to def must be a Symbol
Error message
First argument to def must be a Symbol
What it means
The second element of a def form is the name being interned and must be a Symbol — that is what the compiler interns into the current namespace as a Var. If it is any other type (string, keyword, collection), the compiler cannot create the var and throws immediately.
Solutions
- Use a bare symbol as the name: (def x 1).
- In macros, ensure the name is a symbol: (def ~(symbol name-str) value) or pass 'x quoted.
- To bind a collection, use destructuring in let/loop, not def.
Example fix
// before (def "my-var" 42) // after (def my-var 42)
Defensive patterns
Strategy: type-guard
Validate before calling
(when-not (symbol? name)
(throw (ex-info "def name must be a symbol" {:name name}))) Type guard
(defn valid-def-name? [x] (symbol? x))
Try / catch
try {
(eval `(def ~name ~value))
} catch (RuntimeException e) {
(when (.contains (.getMessage e) "must be a Symbol")
(println "Name position got:" (pr-str name)))
} Prevention
- Quote the name in macros: pass 'x, not x.
- Convert runtime strings with (symbol s) before building def forms.
- Use let destructuring for collections, never def.
When it happens
Trigger: (def "x" 1), (def :x 1), (def [a b] coll), or a macro expansion where the name position received a non-symbol value such as a resolved value or nil-wrapped form.
Common situations: Macros that pass the value instead of the (quoted) name: (def ~name ...) where name was already evaluated to a string; destructuring attempts inside def; dynamic name construction done with strings instead of (symbol ...).
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Can't bind qualified name:" + sym
- params must be Symbols
- Too few arguments to def
- Too many arguments to def
- ArityException
AI-assisted analysis of clojure/clojure@f3b143341d (2026-09-09).
Data as JSON: /api/errors/3f674d0576ce1804.
Report an issue: GitHub.
Appendix: source
Thrown at src/jvm/clojure/lang/Compiler.java:539
public Class getJavaClass(){
return Var.class;
}
static class Parser implements IParser{
public Expr parse(C context, Object form) {
//(def x) or (def x initexpr) or (def x "docstring" initexpr)
String docstring = null;
if(RT.count(form) == 4 && (RT.third(form) instanceof String)) {
docstring = (String) RT.third(form);
form = RT.list(RT.first(form), RT.second(form), RT.fourth(form));
}
if(RT.count(form) > 3)
throw Util.runtimeException("Too many arguments to def");
else if(RT.count(form) < 2)
throw Util.runtimeException("Too few arguments to def");
else if(!(RT.second(form) instanceof Symbol))
throw Util.runtimeException("First argument to def must be a Symbol");
Symbol sym = (Symbol) RT.second(form);
Var v = lookupVar(sym, true);
if(v == null)
throw Util.runtimeException("Can't refer to qualified var that doesn't exist");
if(!v.ns.equals(currentNS()))
{
if(sym.ns == null)
{
v = currentNS().intern(sym);
registerVar(v);
}
// throw Util.runtimeException("Name conflict, can't def " + sym + " because namespace: " + currentNS().name +
// " refers to:" + v);
else
throw Util.runtimeException("Can't create defs outside of current ns");
}
IPersistentMap mm = sym.meta();
boolean isDynamic = RT.booleanCast(RT.get(mm,dynamicKey));View on GitHub (pinned to f3b143341d)