elastic/elasticsearch · error · IllegalStateException
{className} can only be applied to the root project.
Error message
{className} can only be applied to the root project. What it means
Thrown by DockerSupportPlugin.apply(Project) when the plugin is applied to a Gradle subproject instead of the root project. The plugin registers a Gradle shared build service (DockerSupportService) that must exist exactly once per build, so applying it to a non-root project would create duplicate or orphaned service registrations. The guard is a simple identity check: project != project.getRootProject().
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/docker/DockerSupportPlugin.java:43
/**
* Plugin providing {@link DockerSupportService} for detecting Docker installations and determining requirements for Docker-based
* Elasticsearch build tasks.
*/
public class DockerSupportPlugin implements Plugin<Project> {
public static final String DOCKER_SUPPORT_SERVICE_NAME = "dockerSupportService";
public static final String DOCKER_ON_LINUX_EXCLUSIONS_FILE = ".ci/dockerOnLinuxExclusions";
private final ProjectLayout projectLayout;
@Inject
public DockerSupportPlugin(ProjectLayout projectLayout) {
this.projectLayout = projectLayout;
}
@Override
public void apply(Project project) {
if (project != project.getRootProject()) {
throw new IllegalStateException(this.getClass().getName() + " can only be applied to the root project.");
}
project.getPlugins().apply(GlobalBuildInfoPlugin.class);
var buildParams = loadBuildParams(project).get();
Provider<DockerSupportService> dockerSupportServiceProvider = project.getGradle()
.getSharedServices()
.registerIfAbsent(DOCKER_SUPPORT_SERVICE_NAME, DockerSupportService.class, spec -> spec.parameters(params -> {
params.setExclusionsFile(projectLayout.getSettingsDirectory().file(DOCKER_ON_LINUX_EXCLUSIONS_FILE).getAsFile());
params.getIsCI().set(buildParams.getCi());
}));
// Ensure that if we are trying to run Docker build tasks, we assert an available Docker installation exists
project.getGradle().getTaskGraph().whenReady(graph -> {
List<String> dockerTasks = graph.getAllTasks()
.stream()
.filter(task -> task instanceof DockerBuildTask)
.map(Task::getPath)
.collect(Collectors.toList());View on GitHub (pinned to db6a809a66)
Solutions
- Apply DockerSupportPlugin only in the root project's build.gradle or in settings.gradle via the settings plugin mechanism.
- If applying through a convention plugin, ensure the convention plugin itself is only applied to the root project (guard with project == project.getRootProject() before applying DockerSupportPlugin).
- If you need Docker information in a subproject, access the shared service registered by the root project via project.getRootProject().getExtensions().findByType(...) or Gradle shared services lookup instead of re-applying the plugin.
Example fix
// before (subproject build.gradle)
plugins {
id('elasticsearch.docker-support')
}
// after (root build.gradle only)
plugins {
id('elasticsearch.docker-support')
}
// subprojects access Docker info via root-registered service Defensive patterns
Strategy: validation
Validate before calling
// In a convention plugin, validate before applying
if (project == project.getRootProject()) {
project.getPlugins().apply(DockerSupportPlugin.class);
} else {
// access Docker service from root instead
} Prevention
- Always check project == project.getRootProject() before applying root-only plugins.
- Document in the plugin's class-level Javadoc that it is root-only.
- Use Gradle's settings.gradle plugin management to control where plugins are applied.
When it happens
Trigger: Calling project.getPlugins().apply(DockerSupportPlugin.class) (or applying it via the plugins {} block) inside a subproject's build.gradle rather than in the root settings.gradle or root build.gradle. Also triggered if a convention plugin that bundles DockerSupportPlugin is applied to a subproject.
Common situations: A build engineer adds Docker support to a specific subproject's build.gradle thinking it is scoped to that project. A convention plugin in build-tools-internal that transitively applies DockerSupportPlugin is applied per-subproject. A composite build includes the Elasticsearch workspace and the plugin is picked up by an included build's subproject.
Related errors
- {className} can only be applied to the root project.
- {message} you can address this by attending to the reported
- Gradle {minimumGradleVersion}+ is required
- Cannot load VersionPropertiesBuildService
- Cannot resolve minimum compiler version via VersionPropertie
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/a3691ae6f71017d2.
Report an issue: GitHub.