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

  1. Move the metadata to an IMeta target: a symbol (def name ^meta val), or a collection.
  2. 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.
  3. 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

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


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)