elastic/elasticsearch · error · UncheckedIOException
Failed to extract foreign API stubs
Error message
Failed to extract foreign API stubs
What it means
Thrown as an UncheckedIOException inside ExtractForeignApiTask.ExtractionWorkAction.execute() when an IOException occurs while walking the JDK's jrt:/ filesystem (java.lang.foreign classes) or writing the stub JAR via JarOutputStream. The worker reads class files from jrt:/modules/java.base/java/lang/foreign, strips @PreviewFeature annotations, and writes a patched JAR for --patch-module use. Any I/O failure (disk full, broken jrt fs, output dir locked) is wrapped into this message.
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ExtractForeignApiTask.java:187
continue;
}
byte[] stubBytes;
try (InputStream is = Files.newInputStream(file)) {
stubBytes = createStub(is);
}
if (stubBytes == null) {
continue;
}
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 {View on GitHub (pinned to db6a809a66)
Solutions
- Verify the output path (getOutputJar()) is writable and its parent directory can be created; check disk space on the build volume.
- Confirm the worker JVM is a full OpenJDK 21 (not a JRE/stripped runtime) so jrt:/ is available — check getJdk21Launcher() resolves to a real JDK 21.
- Re-run with --info --stacktrace to capture the wrapped IOException's cause, which names the failing path or operation.
- If running on Windows with antivirus, exclude the build directory from scanning, or move the build output to a local non-synced path.
Example fix
// before: output jar resolved to a read-only or network path
getOutputJar().set(project.getLayout().getBuildDirectory().file("libs/native/build/jdk21-foreign-api.jar"));
// after: ensure parent is a local writable dir, force-create before submit
File out = getOutputJar().get().getAsFile();
Files.createDirectories(out.getParentFile().toPath()); Defensive patterns
Strategy: try-catch
Validate before calling
Path out = task.getOutputJar().get().getAsFile().toPath();
if (Files.isWritable(out.getParent()) == false) {
throw new IllegalStateException("Output dir not writable: " + out.getParent());
}
if (FileSystems.getFileSystem(URI.create("jrt:/")) == null) {
throw new IllegalStateException("jrt:/ filesystem unavailable; not a full JDK");
} Try / catch
try {
task.extract();
} catch (UncheckedIOException e) {
// inspect e.getCause() for the underlying IOException
throw new GradleException("Foreign API stub extraction failed: " + e.getCause().getMessage(), e.getCause());
} Prevention
- Ensure getOutputJar() resolves to a local writable path with a creatable parent directory.
- Run the worker on a full OpenJDK 21 (not a JRE) so jrt:/ is available.
- Exclude the build directory from antivirus/backup tools on Windows.
- Verify disk space before running the task on CI agents.
When it happens
Trigger: The try-with-resources block at line 163-188 opens a JarOutputStream on outputPath and walks foreignRoot via Files.walk(). An IOException from Files.walk, Files.newOutputStream, jar.putNextEntry, or jar.write propagates to the catch at line 186 and is rethrown wrapped. Common triggers: output directory not creatable, disk full, jrt:/ filesystem unavailable (non-OpenJDK JVM), concurrent write to the same outputPath.
Common situations: Running the extractForeignApi task under a JRE that lacks the jrt:/ filesystem (some bundled JREs), build output directory on a network filesystem with locking issues, disk-full CI agents, antivirus locking the output JAR on Windows, or a misconfigured getOutputJar() pointing to a read-only location.
Related errors
- ExtractForeignApiTask worker must run on JDK 21 (found JDK $
- Unexpected class file version ${rawVersion} for ${cn.name} (
- Cannot load VersionPropertiesBuildService
- Cannot resolve minimum compiler version via VersionPropertie
- unable to read the git revision
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/594147d967a6d080.
Report an issue: GitHub.