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

  1. Apply ReaperPlugin only in the root project's build.gradle (or settings.gradle applied to the root).
  2. If subprojects need the reaper, register it once at the root and have subprojects consume the shared service by name.
  3. 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

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


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