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

  1. Use a bare symbol as the name: (def x 1).
  2. In macros, ensure the name is a symbol: (def ~(symbol name-str) value) or pass 'x quoted.
  3. 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

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


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)