Browse Learn Clojure Foundations as a Java Developer

Diagnose Java Version Conflicts

Diagnose Clojure setup failures caused by mismatched JDKs, `JAVA_HOME`, IDE runtimes, Maven or Gradle toolchains, and class files compiled for newer Java versions.

Most Clojure code runs on the same JVM your Java code runs on, so Java version conflicts look familiar: the terminal uses one JDK, the editor uses another, Maven or Gradle picks a third, and a class file was compiled for a newer runtime than the one trying to load it.

Start by identifying the actual JVM used by the failing command. Do not assume JAVA_HOME, the IDE, and the shell agree.

Recognize the Symptoms

Symptom Likely cause
UnsupportedClassVersionError A class was compiled for a newer JVM than the runtime currently loading it.
REPL works in terminal but fails in IDE Editor REPL command or project JDK differs from terminal JDK.
Maven tests use one Java version and clojure uses another Build tool, shell, and version manager are not aligned.
Native library or TLS failures appear only on one machine Vendor distribution, architecture, trust store, or old JDK patch level differs.
JAVA_HOME looks correct but java is wrong PATH is finding a different java executable first.

Capture the Runtime Facts

Run these from the same terminal and project directory where the failure occurs:

1java -version
2javac -version
3which java
4which javac
5echo "$JAVA_HOME"
6clojure --version
7clojure -Spath

On macOS, this is also useful:

1/usr/libexec/java_home -V

For Maven and Gradle, ask the tool directly:

1mvn -version
2./gradlew --version

The question is not “what Java do I have installed?” The question is “what Java did this exact command use?”

Align JAVA_HOME and PATH

JAVA_HOME should point to the JDK root. PATH should put that JDK’s bin directory before older or unrelated Java installations.

1export JAVA_HOME=/path/to/jdk
2export PATH="$JAVA_HOME/bin:$PATH"
3
4java -version
5javac -version

On macOS with the system helper:

1export JAVA_HOME=$(/usr/libexec/java_home -v 21)
2export PATH="$JAVA_HOME/bin:$PATH"

Use the version required by your project, not the newest version on your machine. Java 21 remains a common baseline in many JVM teams; Java 25 is also a long-term-support target in many current Java estates. The practical rule is to match the project and CI, then upgrade deliberately.

Version Managers Help, but They Are Not Magic

SDKMAN!, jEnv, asdf, Homebrew, IDE settings, Maven toolchains, and Gradle toolchains can all influence Java selection. Pick one team-supported policy and document it.

Tooling choice Good use
SDKMAN! Developers need quick per-shell switching across JDK distributions.
jEnv Teams want directory-local Java selection layered on installed JDKs.
Maven toolchains Maven builds must compile or test with a specific JDK independent of shell defaults.
Gradle toolchains Gradle should select a Java language/runtime version reproducibly.
IDE project SDK Editor REPL and tests must match the command-line project JDK.

If a tool changes Java selection, add it to the diagnostic list. Hidden version selection is what makes these failures expensive.

Fix UnsupportedClassVersionError

The JVM throws UnsupportedClassVersionError when it tries to read a class file whose class-file version is not supported by the current runtime. In practical terms: something was compiled with a newer Java version than the runtime can load.

Work through this order:

  1. Identify the runtime with java -version from the failing command path.
  2. Identify the build JDK with mvn -version, ./gradlew --version, or CI logs.
  3. Clean generated classes so stale output does not hide the fix.
  4. Rebuild with the intended JDK.
  5. If the dependency itself requires a newer Java version, upgrade the runtime or choose a compatible dependency version.

For Clojure projects, remember that Java dependencies can trigger this even when your own Clojure source has no Java compilation step.

Keep IDE, REPL, and CI Together

Runtime surface What to verify
Terminal REPL The shell PATH, JAVA_HOME, and clojure --version.
IntelliJ/Cursive Project SDK, module SDK, and REPL run configuration.
VS Code/Calva The command Calva uses to start the REPL and its working directory.
Maven mvn -version, compiler settings, and toolchains.
Gradle ./gradlew --version, Java toolchain config, and daemon reuse.
CI The setup step that installs/selects Java before tests run.

When in doubt, make CI the source of truth. Match local tools to CI rather than debugging every machine as a separate environment.

Knowledge Check

### What does `UnsupportedClassVersionError` usually mean? - [x] A class was compiled for a newer Java version than the runtime loading it supports - [ ] Clojure syntax is invalid - [ ] Git has unresolved conflicts - [ ] The namespace file name uses an underscore > **Explanation:** The JVM reports this when class-file version numbers are not supported by the runtime. In Clojure projects, the class may come from a Java dependency or generated output. ### Why should you run `mvn -version` or `./gradlew --version` when debugging Java conflicts? - [x] Build tools may use a different JDK than the terminal command you checked first - [ ] They rewrite `deps.edn` - [ ] They install Clojure automatically - [ ] They delete old Java versions > **Explanation:** Maven, Gradle, IDEs, and shells can each select Java differently. Diagnose the exact failing command path. ### What should `JAVA_HOME` point to? - [x] The JDK root directory - [ ] The project's `src/` directory - [ ] A single `.class` file - [ ] The Maven local repository > **Explanation:** Tools use `JAVA_HOME` to locate the selected JDK; `PATH` should then expose that JDK's `bin` directory.
Revised on Saturday, May 23, 2026