clojure/clojure · error · IllegalArgumentException
Metadata can only be applied to IMetas
Error message
Metadata can only be applied to IMetas
What it means
Thrown by MetaReader after successfully reading metadata when the object it decorates does not implement IMeta. Only symbols, collections, and other IMeta instances can carry metadata; numbers, strings read as literals of non-IMeta types, and other objects cannot. The reader rejects applying ^meta to such a form.
Solutions
- Move the metadata to an IMeta target: a symbol (def name ^meta val), or a collection.
- If you need the value itself tagged, bind it to a named symbol and put metadata there, e.g. (def x ^{:v 1} 42) — metadata on def's symbol.
- Use with-meta only on IObj/IMeta values; restructure code that decorates raw literals.
Example fix
// before
(defn f [] ^{:deprecated true} 42)
// after
(def answer ^{:deprecated true} 42)
(defn f [] answer) Defensive patterns
Strategy: type-guard
Validate before calling
// only apply reader metadata to IMeta targets
function canTakeMeta(o) {
return o instanceof IObj; // IObj extends IMeta
} Type guard
function isIMeta(o) {
return o instanceof IMeta;
} Try / catch
try {
readOrEval(src);
} catch (IllegalArgumentException e) {
if ("Metadata can only be applied to IMetas".equals(e.getMessage())) {
// move metadata to a symbol or collection
} else throw e;
} Prevention
- Never attach ^meta to numbers, strings, or other non-IMeta literals.
- Put metadata on the def symbol or on collections instead.
- Check IMeta before calling with-meta in generated code.
When it happens
Trigger: Reading ^{:doc "x"} 5 or ^:foo [1 2 3] is fine (vectors are IMeta), but ^:foo 42, ^:foo "str" (String is not IMeta in the host), or ^:foo 3.14 trigger it.
Common situations: Attaching type hints to numbers/strings hoping for optimization; macro-generated code that applies metadata to literal values; confusion about which host types implement IMeta.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Metadata can only be applied to IMetas
- Metadata must be Symbol,Keyword,String or Map
- Metadata must be Symbol,Keyword,String,Vector or Map
- arg literal must be %, %& or %integer
- arg literal not in #()
AI-assisted analysis of clojure/clojure@f3b143341d (2026-09-09).
Data as JSON: /api/errors/206c1e1fcc47d23e.
Report an issue: GitHub.
Appendix: source
Thrown at src/jvm/clojure/lang/LispReader.java:986
if(line != -1 && o instanceof ISeq)
{
meta = RT.assoc(meta, RT.LINE_KEY, RT.get(meta, RT.LINE_KEY, line));
meta = RT.assoc(meta, RT.COLUMN_KEY, RT.get(meta,RT.COLUMN_KEY, column));
}
if(o instanceof IReference)
{
((IReference)o).resetMeta((IPersistentMap) meta);
return o;
}
Object ometa = RT.meta(o);
for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
IMapEntry kv = (IMapEntry) s.first();
ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
}
return ((IObj) o).withMeta((IPersistentMap) ometa);
}
else
throw new IllegalArgumentException("Metadata can only be applied to IMetas");
}
}
public static class SyntaxQuoteReader extends AFn{
public Object invoke(Object reader, Object backquote, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
try
{
Var.pushThreadBindings(
RT.map(GENSYM_ENV, PersistentHashMap.EMPTY));
Object form = read(r, true, null, true, opts, ensurePending(pendingForms));
return syntaxQuote(form);
}
finally
{
Var.popThreadBindings();View on GitHub (pinned to f3b143341d)