elastic/elasticsearch · error · IllegalArgumentException
ReaperPlugin can only be applied to the root project of a bu
Error message
ReaperPlugin can only be applied to the root project of a build
What it means
ReaperPlugin.registerReaperService() throws IllegalArgumentException when the project it is applied to is not the root project. The reaper is a Gradle BuildService registered via registerIfAbsent with a fixed name; it must be a build-wide singleton to track all spawned processes across subprojects. Applying it to a subproject would either duplicate the service or scope it incorrectly.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/ReaperPlugin.java:43
/**
* The unique identifier to register the reaper shared service within a gradle build
* */
public static final String REAPER_SERVICE_NAME = "reaper";
private final ProjectLayout projectLayout;
@Inject
ReaperPlugin(ProjectLayout projectLayout) {
this.projectLayout = projectLayout;
}
@Override
public void apply(Project project) {
registerReaperService(project, projectLayout, false);
}
public static void registerReaperService(Project project, ProjectLayout projectLayout, boolean internal) {
if (project != project.getRootProject()) {
throw new IllegalArgumentException("ReaperPlugin can only be applied to the root project of a build");
}
File inputDir = projectLayout.getProjectDirectory()
.dir(".gradle")
.dir("reaper")
.dir("build-" + ProcessHandle.current().pid())
.getAsFile();
project.getGradle().getSharedServices().registerIfAbsent(REAPER_SERVICE_NAME, ReaperService.class, spec -> {
// Provide some parameters
spec.getParameters().getInputDir().set(inputDir);
spec.getParameters().getBuildDir().set(projectLayout.getBuildDirectory());
spec.getParameters().setInternal(internal);
});
}
}
View on GitHub (pinned to db6a809a66)
Solutions
- Apply ReaperPlugin only in the root project's build.gradle (or settings.gradle applied to the root).
- If subprojects need the reaper, register it once at the root and have subprojects consume the shared service by name.
- Use registerReaperService(project.rootProject, ...) to redirect the call to the root if invoked from a subproject context.
Example fix
// before (in submodule build.gradle) apply plugin: 'elasticsearch.reaper' // throws // after (in root build.gradle) apply plugin: 'elasticsearch.reaper'
Defensive patterns
Strategy: validation
Validate before calling
if (project != project.getRootProject()) {
throw new IllegalArgumentException("Apply ReaperPlugin only to the root project");
}
ReaperPlugin.registerReaperService(project, projectLayout, internal); Prevention
- Apply ReaperPlugin in the root build.gradle only.
- Subprojects should consume the root-registered service by name, not re-apply the plugin.
- Use project.getRootProject() to redirect registration if called from a subproject.
When it happens
Trigger: Applying the reaper plugin (`apply plugin: 'elasticsearch.reaper'`) in a subproject's build.gradle, or calling ReaperPlugin.registerReaperService(subproject, ...) directly from build logic in a child project.
Common situations: Copy-pasting a plugin block from a root build.gradle into a submodule; a subproject that spawns external processes wanting its own reaper; misconfigured composite builds where the plugin is applied at the wrong level.
Related errors
- platform cannot be set on elasticsearch distribution [${name
- bundledJdk cannot be set on elasticsearch distribution [${na
- Capturing output is not supported when indentingConsoleOutpu
- Capturing output was not enabled. Use ${name}.getCapturedOut
- Cannot set commandline with empty list.
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/1c7ea93bc5922dee.
Report an issue: GitHub.