elastic/elasticsearch · critical · IllegalStateException

Unexpected class file version ${rawVersion} for ${cn.name} (

Error message

Unexpected class file version ${rawVersion} for ${cn.name} (expected ${V21} / JDK 21)

What it means

Thrown as IllegalStateException by checkJava21(ClassNode) when (cn.version & 0xFFFF) != V21 (65). Before stripping the preview flag and @PreviewFeature annotations from a java.lang.foreign class, the worker asserts the input class file is exactly major version 65 (JDK 21). A different version means the jrt:/ image is not a JDK 21 image, and the stub-generation logic (which assumes preview semantics) would produce an incorrect JAR.

Source

Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ExtractForeignApiTask.java:260

            cn.innerClasses.removeIf(ic -> (ic.access & Opcodes.ACC_PUBLIC) == 0);

            ClassWriter writer = new ClassWriter(0);
            cn.accept(writer);
            return writer.toByteArray();
        }

        private static List<AnnotationNode> stripPreviewAnnotations(List<AnnotationNode> anns) {
            if (anns == null) {
                return null;
            }
            anns.removeIf(a -> a.desc.equals(PREVIEW_FEATURE_DESCRIPTOR));
            return anns.isEmpty() ? null : anns;
        }

        private static void checkJava21(ClassNode cn) {
            int rawVersion = cn.version & 0xFFFF;
            if (rawVersion != V21) {
                throw new IllegalStateException(
                    "Unexpected class file version " + rawVersion + " for " + cn.name + " (expected " + V21 + " / JDK 21)"
                );
            }
        }
    }
}

View on GitHub (pinned to db6a809a66)

Solutions

  1. Confirm the JDK 21 in use is a stable GA release (Temurin/Zulu/Corretto 21.x GA) — re-point JAVA_HOME or the toolchain to a GA JDK 21.
  2. Inspect the offending class with javap -v against the JDK image to see its actual major version and confirm whether the image is corrupted.
  3. If the project upgraded the foreign stub expectations, update V21 handling in ExtractForeignApiTask to match the new target version.
  4. Reinstall the JDK 21 installation if a custom/patched module replaced java.base.

Example fix

// before: EA JDK 21 with foreign classes at version 66
// throws: Unexpected class file version 66 for java/lang/foreign/MemorySegment

// after: switch toolchain to a GA JDK 21 where foreign classes are version 65
javaToolchainService.launcherFor {
  it.languageVersion.set(JavaLanguageVersion.of(21))
  it.vendor.set(JvmVendor.ADOPTIUM)
}.let { task.getJdk21Launcher().set(it) }
Defensive patterns

Strategy: validation

Validate before calling

ClassReader reader = new ClassReader(classStream);
int major = (reader.readUnsignedShort(6)) & 0xFFFF;  // class file major version offset
if (major != 65 /* V21 */) {
    throw new IllegalStateException("Refusing to process non-JDK21 class file (version " + major + ")");
}

Prevention

When it happens

Trigger: createStub() (line 205) reads a class from jrt:/modules/java.base/java/lang/foreign, parses it with ASM, and calls checkJava21(cn) at line 209. If the runtime is JDK 21 but the class file's major version isn't 65 — e.g., a backported/custom JDK image, a corrupted module, or an unexpectedly updated JDK where foreign classes shifted versions — the assertion fires.

Common situations: Using an early-access or custom-built JDK 21 where class file versions differ; a JDK image patched with classes from a different version; running against a non-standard distribution (GraalVM, Zulu) where foreign package classes carry a different version tag in EA builds.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/780fdbfe411ae48c. Report an issue: GitHub.