elastic/elasticsearch · error · UncheckedIOException
Unable to create reaper JAR output directory {}
Error message
Unable to create reaper JAR output directory {} What it means
ReaperService.locateReaperJar() throws UncheckedIOException (non-internal mode) when Files.createDirectories(jarPath.getParent()) fails while preparing buildDir/reaper/ to receive the copied reaper.jar. The parent directory is <buildDir>/reaper/; the failure wraps the original IOException with the target path so the filesystem cause (permissions, quota, read-only mount) is identifiable.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/ReaperService.java:146
if (getParameters().getInternal()) {
// when running inside the Elasticsearch build just pull find the jar in the runtime classpath
URL main = this.getClass().getClassLoader().getResource(REAPER_CLASS);
String mainPath = main.getFile();
Matcher matcher = REAPER_JAR_PATH_PATTERN.matcher(mainPath);
if (matcher.matches()) {
String path = matcher.group(1);
return Path.of(OS.<String>conditional().onWindows(() -> path.substring(1)).onUnix(() -> path).supply());
} else {
throw new RuntimeException("Unable to locate " + REAPER_CLASS + " on build classpath.");
}
} else {
// copy the reaper jar
Path jarPath = getParameters().getBuildDir().get().getAsFile().toPath().resolve("reaper").resolve("reaper.jar");
try {
Files.createDirectories(jarPath.getParent());
} catch (IOException e) {
throw new UncheckedIOException("Unable to create reaper JAR output directory " + jarPath.getParent(), e);
}
try (
OutputStream out = Files.newOutputStream(jarPath);
InputStream jarInput = this.getClass().getResourceAsStream("/META-INF/reaper.jar");
) {
logger.info("Copying reaper.jar...");
jarInput.transferTo(out);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return jarPath;
}
}
private void ensureReaperAlive() {
if (reaperProcess.isAlive() == false) {View on GitHub (pinned to db6a809a66)
Solutions
- Check the wrapped IOException's message for the filesystem reason (Permission denied, No space left on device, Read-only file system).
- Ensure buildDir is writable by the Gradle process and not on a read-only mount.
- Free disk space or raise quota if the volume is full.
- Remove a conflicting non-directory file at <buildDir>/reaper if one exists.
- If running in internal mode is appropriate, switch Params.internal to true to bypass the copy entirely.
Defensive patterns
Strategy: validation
Validate before calling
Path reaperDir = buildDir.toPath().resolve("reaper");
if (!Files.isWritable(buildDir.toPath())) {
throw new IllegalStateException("buildDir is not writable: " + buildDir);
} Try / catch
try { Files.createDirectories(reaperDir); } catch (IOException e) { throw new UncheckedIOException("Cannot create " + reaperDir, e); } Prevention
- Ensure the build directory is writable and not on a read-only mount.
- Free disk space if the volume is full.
- Resolve any non-directory file conflicting with <buildDir>/reaper/.
- Switch to Params.internal = true to bypass the JAR copy when feasible.
When it happens
Trigger: Non-internal mode (Params.internal = false): the service tries to materialize buildDir/reaper/reaper.jar from the bundled /META-INF/reaper.jar resource, and creating buildDir/reaper/ fails.
Common situations: Read-only build directory (CI with a locked workspace, container with a read-only mount); insufficient permissions on the build dir; disk full or quota exceeded; the buildDir was deleted concurrently; parent path conflicts with an existing file (not a directory).
Related errors
- Cannot create directory '%s' as it already exists, but is no
- Cannot create parent directory '%s' when creating directory
- Failed to create parent directory '%s' when creating directo
- Failed to create directory '%s'
- ReaperPlugin can only be applied to the root project of a bu
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/4c99a0f1ad5a2732.
Report an issue: GitHub.