Skip to content

Java25Parser compiled with Java 25 breaks Java 24 runtime compatibility #6644

@kdvolder

Description

@kdvolder

Java25Parser compiled with Java 25 breaks Java 24 runtime compatibility

What version of OpenRewrite are you using?

  • OpenRewrite core: 8.71.0 (also reproduced with 8.73.0-SNAPSHOT)
  • Maven plugin: 6.27.1
  • Gradle plugin: 7.19.0
  • Bug NOT present in OpenRewrite 8.65.0
  • Bug introduced sometime between 8.65.0 and 8.71.0 (intermediate versions not tested)

How are you running OpenRewrite?

I am using both the Maven and Gradle plugins. The issue affects both single-module and multi-module projects.

Maven configuration:

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>6.27.1</version>
  <configuration>
    <activeRecipes>
      <recipe>com.vmware.tanzu.spring.recipes.boot40.UpgradeSpringBoot_4_0</recipe>
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>com.vmware.tanzu.spring.recipes</groupId>
      <artifactId>spring-boot-4-upgrade-recipes</artifactId>
      <version>1.5.5-SNAPSHOT</version>
    </dependency>
  </dependencies>
</plugin>

Gradle configuration:

plugins {
    id("org.openrewrite.rewrite") version "7.19.0"
}

rewrite {
    activeRecipe("com.vmware.tanzu.spring.recipes.boot40.UpgradeSpringBoot_4_0")
}

dependencies {
    rewrite("com.vmware.tanzu.spring.recipes:spring-boot-4-upgrade-recipes:1.5.5-SNAPSHOT")
}

Reproduction project: https://github.com/vmware-tanzu/spring-upgrade-recipes/tree/main/bug-reports/java25parser-gradle

What is the smallest, simplest way to reproduce the problem?

The issue occurs when running any OpenRewrite recipe on Java 24 runtime. Even a minimal project triggers the error:

build.gradle:

plugins {
    id 'java'
    id 'org.openrewrite.rewrite' version '7.19.0'
}

rewrite {
    activeRecipe("org.openrewrite.java.format.AutoFormat")
}

Any Java file:

public class Example {
    public void hello() {
        System.out.println("Hello");
    }
}

Run with Java 24:

$ java -version
openjdk version "24" 2025-03-18
OpenJDK Runtime Environment (build 24+36)

$ ./gradlew rewriteRun

What did you expect to see?

The recipe should execute successfully on Java 24 runtime, just as it does on Java 21 and Java 25.

What did you see instead?

The build fails immediately with a classversion error:

java.lang.UnsupportedClassVersionError: org/openrewrite/java/Java25Parser has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 68.0

What is the full stack trace of any errors you encountered?

java.lang.UnsupportedClassVersionError: org/openrewrite/java/Java25Parser has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 68.0
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
	at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
	at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
	at org.openrewrite.java.JavaParser.fromJavaVersion(JavaParser.java:XXX)

Root Cause Analysis

A version of OpenRewrite between 8.65.0 and 8.71.0 introduced two changes that together create this issue:

1. Java25Parser recompiled with Java 25

The rewrite-java-25 module was recompiled from Java 21 (class version 65) to Java 25 (class version 69).

Verified via bytecode inspection:

# OpenRewrite 8.65.0
$ javap -v Java25Parser.class | grep "major version"
  major version: 65  # Java 21

# OpenRewrite 8.71.0  
$ javap -v Java25Parser.class | grep "major version"
  major version: 69  # Java 25

2. Parser selection logic changed

In rewrite-java/src/main/java/org/openrewrite/java/JavaParser.java (commit 07a72d60a):

Before (8.65.0):

if (version > 21) {
    return new Java25Parser(...);
}

After (8.71.0):

if (version > 22) {
    return new Java25Parser(...);
}

The version variable comes from System.getProperty("java.version") - it's the JVM runtime version, not the source level.

Impact by Java version:

  • Java 21 (LTS): ✅ Works - condition is false, never loads Java25Parser
  • Java 24: ❌ BREAKS - condition is true, attempts to load Java25Parser compiled with Java 25
  • Java 25: ✅ Works - can load Java25Parser compiled with Java 25

Suggested Fixes

Option 1: Compile Java25Parser with Java 21 (Recommended)

Revert to compiling the rewrite-java-25 module with Java 21 (as in 8.65.0). This maintains backward compatibility while still supporting Java 25 source code parsing.

Option 2: Adjust parser selection logic

Change the condition to only load Java25Parser on Java 25+:

if (version > 24) {
    return new Java25Parser(...);
}

Business Impact

While Java 24 is a non-LTS interim release, some organizations require Java 24 support for:

  • Proactive compatibility testing before Java 25 LTS
  • Early adoption of new language features
  • Enterprise CI/CD pipelines testing against multiple Java versions

This issue forced us to downgrade from 8.71.0 to 8.65.0 to maintain Java 24 compatibility for our customers ahead of an upcoming release.

Are you interested in contributing a fix to OpenRewrite?

We're happy to contribute a fix if guidance is provided on the preferred approach (Option 1 vs Option 2 above). Please let us know which direction you'd like to take.

Attachement

I am attaching a zip file containing scripts and test code that can be used to reproduce the bug.

java25parser-gradle.zip

Note: name is confusing it says 'gradle' but it actually uses maven, originally we did also reproduce with gradle but 'parameters' such as OR version are easier to control with maven, and the bug doesn't appear to be specific to a particular plugin (maven/gradle) or version of the plugin.

Note: created attached reproducer and analysis with help of AI, but should contain ample material and easy to follow instructions to figure out how to run it. It has a script which you are meant to run, give it parameter which is a Java version (21, 24, 25) and it will run the maven plugin on a toy project.

It uses sdkman and assumes you have required JDK installed to run the test script.

Also includes detailed analysis on which this bug report is based.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions