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
- Add matching primitive type hints to the value expression (e.g. ^long on args/return) so it can emit unboxed
- Use an explicitly primitive-producing function like (unchecked-inc x) with hints
- If primitives can't be guaranteed, make the field non-primitive (drop ^long/^double hint)
- 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
- Type-hint the whole chain feeding a primitive mutable local
- Only assign expressions known to return primitives (arith on hinted values)
- Avoid assigning collection-derived values to primitive mutables
- Use (set! x (unchecked-long v)) style with matching hints
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
- Cannot coerce to , use a cast instead
- Mismatched primitive return, expected: , had:
- Only long and double primitives are supported
- Only long and double primitives are supported:
- & arg cannot have type hint
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)