clojure/clojure · error · RuntimeException
Unexpected number of constructor arguments to
Error message
Unexpected number of constructor arguments to {}: got {} What it means
When reading the short positional form #RecordName[...] the reader scans the record class's constructors for one whose parameter count matches the number of supplied arguments. If no constructor accepts that arity, it throws listing the class and the argument count received. This happens after class lookup succeeds, so the record name is valid but the arity is wrong.
Solutions
- Match the argument count to the record's field count: #user.Point[1 2] for a two-field record.
- Reload/redefine the record consistently and re-serialize the data (pr-str) from a live instance.
- If extra data must be carried, use the map form #user.Point{:k v} and keep extra keys in the record's extmap-safe behavior, or move them to metadata.
Example fix
// before #user.Point[1 2 3] ; Point has 2 fields // after #user.Point[1 2]
Defensive patterns
Strategy: validation
Validate before calling
(defn arity-matches? [record-class n]
(some #(= (count (.getParameterTypes ^java.lang.reflect.Constructor %)) n)
(.getConstructors record-class))) Try / catch
(try (read-string lit) (catch RuntimeException e (throw (ex-info "record arity mismatch" {:literal lit} e)))) Prevention
- Regenerate serialized literals whenever defrecord fields change
- Keep record serialization and definition in the same versioned artifact
- Prefer the map form #Rec{:k v} in serialized data
When it happens
Trigger: Reading #user.Point[1 2 3] when the defrecord Point has only 2 fields (constructors of arity 2 plus the arity-0 factory), or #user.Point[] with a record having required fields.
Common situations: Record definition changed (fields added/removed) after data was serialized; hand-edited serialized records; copy-pasting literals from a differently-versioned namespace.
Related errors
- Unreadable constructor form starting with "#
- Unreadable defrecord form: key must be of type…
- arg literal must be %, %& or %integer
- arg literal not in #()
- ArityException
AI-assisted analysis of clojure/clojure@f3b143341d (2026-09-09).
Data as JSON: /api/errors/8ddbc5074858e950.
Report an issue: GitHub.
Appendix: source
Thrown at src/jvm/clojure/lang/LispReader.java:1487
} else if (form instanceof IPersistentVector) {
shortForm = true;
} else {
throw Util.runtimeException("Unreadable constructor form starting with \"#" + recordName + "\"");
}
Object ret = null;
Constructor[] allctors = ((Class)recordClass).getConstructors();
if(shortForm)
{
IPersistentVector recordEntries = (IPersistentVector)form;
boolean ctorFound = false;
for (Constructor ctor : allctors)
if(ctor.getParameterTypes().length == recordEntries.count())
ctorFound = true;
if(!ctorFound)
throw Util.runtimeException("Unexpected number of constructor arguments to " + recordClass.toString() + ": got " + recordEntries.count());
ret = Reflector.invokeConstructor(recordClass, RT.toArray(recordEntries));
}
else
{
IPersistentMap vals = (IPersistentMap)form;
for(ISeq s = RT.keys(vals); s != null; s = s.next())
{
if(!(s.first() instanceof Keyword))
throw Util.runtimeException("Unreadable defrecord form: key must be of type clojure.lang.Keyword, got " + s.first().toString());
}
ret = Reflector.invokeStaticMethod(recordClass, "create", new Object[]{vals});
}
return ret;
}
}View on GitHub (pinned to f3b143341d)