elastic/elasticsearch · error · UncheckedIOException

Failed to create output directory

Error message

Failed to create output directory

What it means

Thrown by ExtractForeignApiTask.ExtractionWorkAction.execute when Files.createDirectories(outputPath.getParent()) fails with IOException, wrapped as UncheckedIOException. The worker extracts java.lang.foreign classes from the running JDK's jrt:/ image into an output jar, so it first needs the output jar's parent directory to exist.

Source

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

    /**
     * Extraction logic that executes inside the forked JDK 21 worker process.
     * Reads {@code java.lang.foreign} classes from the worker's own {@code jrt:/} image, so there
     * are no cross-JDK module-format compatibility concerns.
     */
    public abstract static class ExtractionWorkAction implements WorkAction<ExtractionParameters> {

        private static final Logger LOGGER = Logging.getLogger(ExtractionWorkAction.class);

        @Override
        public void execute() {
            checkRuntimeJava21();

            Path outputPath = getParameters().getOutputJar().getAsFile().get().toPath();
            try {
                Files.createDirectories(outputPath.getParent());
            } catch (IOException e) {
                throw new UncheckedIOException("Failed to create output directory", e);
            }

            FileSystem jrtFs = FileSystems.getFileSystem(URI.create("jrt:/"));
            Path foreignRoot = jrtFs.getPath("modules", "java.base", "java", "lang", "foreign");

            int count = 0;
            try (
                JarOutputStream jar = new JarOutputStream(Files.newOutputStream(outputPath));
                Stream<Path> walk = Files.walk(foreignRoot)
            ) {
                for (Path file : (Iterable<Path>) walk::iterator) {
                    if (Files.isRegularFile(file) == false || file.getFileName().toString().endsWith(".class") == false) {
                        continue;
                    }
                    byte[] stubBytes;
                    try (InputStream is = Files.newInputStream(file)) {
                        stubBytes = createStub(is);
                    }

View on GitHub (pinned to db6a809a66)

Solutions

  1. Check the outputJar task property resolves to a writable, valid path.
  2. Ensure no regular file occupies the intended parent directory.
  3. Verify write permissions on the build output root.
  4. Clean the build outputs so stale files are removed.
Defensive patterns

Strategy: validation

Validate before calling

Path parent = getParameters().getOutputJar().getAsFile().get().toPath().getParent();
if (Files.exists(parent) == false && parent != null) {
    // will attempt createDirectories; check writability of nearest existing ancestor
    Path ancestor = parent;
    while (ancestor != null && Files.exists(ancestor) == false) ancestor = ancestor.getParent();
    if (ancestor != null && Files.isWritable(ancestor) == false) {
        throw new UncheckedIOException("Cannot create output directory " + parent, new IOException("not writable"));
    }
}

Try / catch

try {
    Files.createDirectories(outputPath.getParent());
} catch (IOException e) {
    throw new UncheckedIOException("Failed to create output directory", e);
}

Prevention

When it happens

Trigger: The configured outputJar path has a parent that cannot be created - invalid path, permission denied, or a parent component names an existing non-directory file.

Common situations: outputJar property misconfigured to an unwritable or invalid path; build output dir on read-only filesystem; stale file occupying the parent path.

Related errors


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