clojure/clojure · error · RuntimeException

fns taking primitives cannot be variadic

Error message

fns taking primitives cannot be variadic

What it means

Clojure only supports primitive arities for non-variadic functions, and only for long/double parameters. If a fn has a primitive-typed signature (method.prim set) and also a rest parameter, the compiler rejects the combination because primitive arities cannot be variadic.

Solutions

  1. Remove the rest parameter so the fn has fixed arity with primitive params
  2. Remove the primitive hints so the fn is variadic but boxed
  3. Split into two functions: a fixed-arity primitive version and a variadic boxed version
  4. Keep primitive hints only on long/double and only in fixed-arity fns

Example fix

;; before
(defn f [^long x & more] (+ x (first more)))
;; after
(defn f [^long x] (inc x))
(defn f-variadic [x & more] (apply + x more))
Defensive patterns

Strategy: validation

Validate before calling

(defn primitive-variadic? [params]
  (and (some #{'&} params)
       (some #(contains? (meta %) :tag) params)))
;; if true, either drop the hint or drop the rest param

Prevention

When it happens

Trigger: Defining (defn f [^long x & rest] ...) or (fn ^long [^double d & more] ...) where a primitive-tagged positional param coexists with a rest param (Compiler.java:6002).

Common situations: Trying to write a high-performance variadic primitive function; adding a & rest param to an existing primitive-arity fn for backward compatibility.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of clojure/clojure@f3b143341d (2026-09-09). Data as JSON: /api/errors/ebdb12e6f69cc48b. Report an issue: GitHub.

Appendix: source

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

						throw Util.runtimeException("Invalid parameter list");
					}

				else
					{
					Class pc = primClass(tagClass(tagOf(p)));
//					if(pc.isPrimitive() && !canBeDirect)
//						{
//						pc = Object.class;
//						p = (Symbol) ((IObj) p).withMeta((IPersistentMap) RT.assoc(RT.meta(p), RT.TAG_KEY, null));
//						}
//						throw Util.runtimeException("Non-static fn can't have primitive parameter: " + p);
					if(pc.isPrimitive() && !(pc == double.class || pc == long.class))
						throw new IllegalArgumentException("Only long and double primitives are supported: " + p);

					if(state == PSTATE.REST && tagOf(p) != null)
						throw Util.runtimeException("& arg cannot have type hint");
					if(state == PSTATE.REST && method.prim != null)
						throw Util.runtimeException("fns taking primitives cannot be variadic");
					                        
					if(state == PSTATE.REST)
						pc = ISeq.class;
					argtypes.add(Type.getType(pc));
					argclasses.add(pc);
					LocalBinding lb = pc.isPrimitive() ?
					                  registerLocal(p, null, new MethodParamExpr(pc), true)
					                           : registerLocal(p, state == PSTATE.REST ? ISEQ : tagOf(p), null, true);
					argLocals = argLocals.cons(lb);
					switch(state)
						{
						case REQ:
							method.reqParms = method.reqParms.cons(lb);
							break;
						case REST:
							method.restParm = lb;
							state = PSTATE.DONE;
							break;

View on GitHub (pinned to f3b143341d)