clojure/clojure · error · java.lang.IllegalArgumentException

Must assign primitive to primitive mutable:

Error message

Must assign primitive to primitive mutable: 

What it means

Within emitAssignLocal, if the local binding is a primitive mutable (e.g. ^:mutable ^long x), the assigned value expression must itself be able to emit an unboxed primitive of matching type. Assigning a boxed/Object-valued expression to a primitive mutable throws this IllegalArgumentException — Clojure refuses to silently box/unbox in this path.

Solutions

  1. Add matching primitive type hints to the value expression (e.g. ^long on args/return) so it can emit unboxed
  2. Use an explicitly primitive-producing function like (unchecked-inc x) with hints
  3. If primitives can't be guaranteed, make the field non-primitive (drop ^long/^double hint)
  4. Restructure loop/recur code so values stay primitive throughout

Example fix

// before
(deftype T [^:mutable ^long x]
  P (set-from [this v] (set! x v)))  ; v untagged -> Object
// after
(deftype T [^:mutable ^long x]
  P (set-from [this ^long v] (set! x v)))
Defensive patterns

Strategy: type-guard

Validate before calling

(defn primitive-assignable? [^Class primc val-tag]
  (and (contains? #{long double} primc)
       (= primc val-tag)))

Try / catch

(try
  (compile 'myns)
  (catch IllegalArgumentException e
    (when-not (re-find #"Must assign primitive to primitive mutable" (.getMessage e)) (throw e))
    (log/error "value expr cannot emit primitive; add ^long/^double hints")))

Prevention

When it happens

Trigger: (set! prim-local boxedExpr) where prim-local is ^:mutable ^long/^double but the value expression cannot emit a primitive (e.g. the value comes from a function returning Object, a boxed intermediate, or an untagged computation).

Common situations: Assigning the result of a generic function (returning Object) to a ^long local; missing type hints in a chain of calls feeding the set!; looping with values derived from collections (which are always boxed).

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/13289d3626a96cde. Report an issue: GitHub.

Appendix: source

Thrown at src/jvm/clojure/lang/Compiler.java:5657

    Class jc;
	public Class getJavaClass() {
		if (jc == null)
            jc = (compiledClass != null) ? compiledClass
                : (tag != null) ? HostExpr.tagToClass(tag)
                : IFn.class;
        return jc;
	}

	public void emitAssignLocal(GeneratorAdapter gen, LocalBinding lb,Expr val){
		if(!isMutable(lb))
			throw new IllegalArgumentException("Cannot assign to non-mutable: " + lb.name);
		Class primc = lb.getPrimitiveType();
		gen.loadThis();
		if(primc != null)
			{
			if(!(val instanceof MaybePrimitiveExpr && ((MaybePrimitiveExpr) val).canEmitPrimitive()))
				throw new IllegalArgumentException("Must assign primitive to primitive mutable: " + lb.name);
			MaybePrimitiveExpr me = (MaybePrimitiveExpr) val;
			me.emitUnboxed(C.EXPRESSION, this, gen);
			gen.putField(objtype, lb.name, Type.getType(primc));
			}
		else
			{
			val.emit(C.EXPRESSION, this, gen);
			gen.putField(objtype, lb.name, OBJECT_TYPE);
			}
	}

	private void emitLocal(GeneratorAdapter gen, LocalBinding lb, boolean clear){
		if(closes.containsKey(lb))
			{
			Class primc = lb.getPrimitiveType();
			gen.loadThis();
			if(primc != null)
				{

View on GitHub (pinned to f3b143341d)