clojure/clojure · error · IllegalArgumentException

Unexpected switch type

Error message

Unexpected switch type: ${switchType}

What it means

The SwitchExpr constructor validates that switchType is one of the two known constants (compactKey or sparseKey) generated by the case* macroexpansion. Receiving any other value means the internal contract between the case macro and the compiler expression node is broken, so an IllegalArgumentException is thrown.

Solutions

  1. Check the classpath for duplicate/mismatched clojure jars (mvn dependency:tree) and pin a single consistent Clojure version.
  2. Verify no local macro shadows clojure.core/case or redefines case*; use fully-qualified clojure.core/case if needed.
  3. If you wrote a custom switch emitter, pass only :compact or :sparse as :switch-type.

Example fix

// before
(defmacro case2 [& body] `(case* ~@body ... :switch-type :weird ...))
// after
(defmacro case2 [& body] `(case* ~@body ... :switch-type :sparse ...))
Defensive patterns

Strategy: try-catch

Try / catch

(try
  (case x :a 1 :b 2)
  (catch IllegalArgumentException e
    (if (re-find #"Unexpected switch type" (.getMessage e))
      (do (println "broken case macro / mismatched clojure jars")
          (fallback-dispatch x))
      (throw e))))

Prevention

When it happens

Trigger: case*/switch* macroexpansion emitting a :switch-type other than :compact or :sparse, e.g. from a broken/patched core macro or macro overrides that shadow clojure.core/case with a buggy implementation.

Common situations: Using a library or custom macro that redefines case or case*; bytecode/compiler version mismatch (a newer clojure.jar with older core.clj on the classpath or vice versa); experimental branch code.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

    final static Keyword sparseKey = Keyword.intern(null, "sparse");
    final static Keyword hashIdentityKey = Keyword.intern(null, "hash-identity");
    final static Keyword hashEquivKey = Keyword.intern(null, "hash-equiv");
    final static Keyword intKey = Keyword.intern(null, "int");
	//(case* expr shift mask default map<minhash, [test then]> table-type test-type skip-check?)
	public CaseExpr(int line, int column, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr,
	        SortedMap<Integer,Expr> tests,HashMap<Integer,Expr> thens, Keyword switchType, Keyword testType, Set<Integer> skipCheck){
		this.expr = expr;
		this.shift = shift;
		this.mask = mask;
		this.low = low;
		this.high = high;
		this.defaultExpr = defaultExpr;
		this.tests = tests;
		this.thens = thens;
		this.line = line;
		this.column = column;
		if (switchType != compactKey && switchType != sparseKey)
		    throw new IllegalArgumentException("Unexpected switch type: "+switchType);
		this.switchType = switchType;
        if (testType != intKey && testType != hashEquivKey && testType != hashIdentityKey)
            throw new IllegalArgumentException("Unexpected test type: "+switchType);
		this.testType = testType;
		this.skipCheck = skipCheck;
		Collection<Expr> returns = new ArrayList(thens.values());
		returns.add(defaultExpr);
		this.returnType = maybeJavaClass(returns);
        if(RT.count(skipCheck) > 0 && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
            {
            RT.errPrintWriter()
              .format("Performance warning, %s:%d:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n",
                      SOURCE_PATH.deref(), line, column);
            }
	}

	public boolean hasJavaClass(){
	    return returnType != null;

View on GitHub (pinned to f3b143341d)