elastic/elasticsearch · critical · IllegalStateException
ExtractForeignApiTask worker must run on JDK 21 (found JDK $
Error message
ExtractForeignApiTask worker must run on JDK 21 (found JDK ${jdkVersion}). The Foreign Function & Memory API is preview in JDK 21 only; on JDK 22+ it is standard and this stub JAR is unnecessary. What it means
Thrown as IllegalStateException by checkRuntimeJava21() at the start of ExtractionWorkAction.execute() when Runtime.version().feature() != 21. The task exists solely because the Foreign Function & Memory API is @PreviewFeature in JDK 21; on JDK 22+ it is standard and the stub JAR is pointless. The guard prevents producing a stub from the wrong class-file version or failing silently on a runtime that lacks the preview API.
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ExtractForeignApiTask.java:196
String entryName = FOREIGN_PACKAGE_PREFIX + file.getFileName().toString();
JarEntry entry = new JarEntry(entryName);
entry.setTime(0);
jar.putNextEntry(entry);
jar.write(stubBytes);
jar.closeEntry();
count++;
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to extract foreign API stubs", e);
}
LOGGER.info("Generated {} with {} class(es)", outputPath, count);
}
private static void checkRuntimeJava21() {
int jdkVersion = Runtime.version().feature();
if (jdkVersion != 21) {
throw new IllegalStateException(
"ExtractForeignApiTask worker must run on JDK 21 (found JDK "
+ jdkVersion
+ "). "
+ "The Foreign Function & Memory API is preview in JDK 21 only; on JDK 22+ it is standard and this stub JAR is unnecessary."
);
}
}
static byte[] createStub(InputStream classStream) throws IOException {
ClassReader reader = new ClassReader(classStream);
ClassNode cn = new ClassNode();
reader.accept(cn, 0);
checkJava21(cn);
if ((cn.access & Opcodes.ACC_PUBLIC) == 0) {
return null;
}
View on GitHub (pinned to db6a809a66)
Solutions
- Run the Gradle daemon on JDK 21 (JAVA_HOME points to JDK 21) so the in-process noIsolation path is correct.
- Ensure a JDK 21 toolchain is auto-detected by Gradle so ForeignApiPlugin populates getJdk21Launcher() and forks the worker with it.
- If the project has moved past JDK 21 entirely, remove/skip the ExtractForeignApiTask since the stub JAR is unnecessary on JDK 22+.
- Check ~/.gradle/gradle.properties and org.gradle.java.installations.* paths include a JDK 21 install.
Example fix
// before: daemon on JDK 22, no launcher supplied → throws
workQueue = workerExecutor.noIsolation();
// after: force a JDK 21 toolchain launcher so the worker forks on 21
javaToolchainService.launcherFor { it.languageVersion.set(JavaLanguageVersion.of(21)) }
.get().let { task.getJdk21Launcher().set(it) } Defensive patterns
Strategy: validation
Validate before calling
int feature = Runtime.version().feature();
if (feature != 21) {
System.err.println("WARNING: ExtractForeignApiTask requires JDK 21, found JDK " + feature);
// supply a JDK 21 toolchain launcher or skip the task
} Prevention
- Pin the Gradle daemon JAVA_HOME to JDK 21, or configure a JDK 21 toolchain so ForeignApiPlugin forks the worker.
- Add org.gradle.java.installations.paths pointing to a JDK 21 install in gradle.properties.
- On JDK 22+, remove or disable the ExtractForeignApiTask since the stub is unnecessary.
- Document the JDK 21 requirement in CI setup scripts.
When it happens
Trigger: The worker action calls checkRuntimeJava21() unconditionally (line 150). If getJdk21Launcher() was absent (daemon assumed to be JDK 21) but the daemon is actually JDK 22+, or if the forked process launcher points at a non-21 JDK, this throws. Also hit in unit tests that construct the action without a real JDK 21 runtime.
Common situations: Developer upgraded their default JAVA_HOME to JDK 22+ but ForeignApiPlugin's toolchain detection didn't supply a JDK 21 launcher; CI image rolled forward to a newer JDK; running the worker in-process via noIsolation() on a daemon started with JDK 17 or 25.
Related errors
- Failed to extract foreign API stubs
- Unexpected class file version ${rawVersion} for ${cn.name} (
- Operating system {}
- Architecture {}
- Jvm Metadata cannot be resolved for {}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/98fcc75217897dc2.
Report an issue: GitHub.