{"record":{"id":"a3691ae6f71017d2","repo":"elastic/elasticsearch","slug":"classname-can-only-be-applied-to-the-root-projec","errorCode":null,"errorMessage":"{className} can only be applied to the root project.","messagePattern":"(.+?) can only be applied to the root project\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/docker/DockerSupportPlugin.java","lineNumber":43,"sourceCode":"/**\n * Plugin providing {@link DockerSupportService} for detecting Docker installations and determining requirements for Docker-based\n * Elasticsearch build tasks.\n */\npublic class DockerSupportPlugin implements Plugin<Project> {\n    public static final String DOCKER_SUPPORT_SERVICE_NAME = \"dockerSupportService\";\n    public static final String DOCKER_ON_LINUX_EXCLUSIONS_FILE = \".ci/dockerOnLinuxExclusions\";\n\n    private final ProjectLayout projectLayout;\n\n    @Inject\n    public DockerSupportPlugin(ProjectLayout projectLayout) {\n        this.projectLayout = projectLayout;\n    }\n\n    @Override\n    public void apply(Project project) {\n        if (project != project.getRootProject()) {\n            throw new IllegalStateException(this.getClass().getName() + \" can only be applied to the root project.\");\n        }\n        project.getPlugins().apply(GlobalBuildInfoPlugin.class);\n        var buildParams = loadBuildParams(project).get();\n\n        Provider<DockerSupportService> dockerSupportServiceProvider = project.getGradle()\n            .getSharedServices()\n            .registerIfAbsent(DOCKER_SUPPORT_SERVICE_NAME, DockerSupportService.class, spec -> spec.parameters(params -> {\n                params.setExclusionsFile(projectLayout.getSettingsDirectory().file(DOCKER_ON_LINUX_EXCLUSIONS_FILE).getAsFile());\n                params.getIsCI().set(buildParams.getCi());\n            }));\n\n        // Ensure that if we are trying to run Docker build tasks, we assert an available Docker installation exists\n        project.getGradle().getTaskGraph().whenReady(graph -> {\n            List<String> dockerTasks = graph.getAllTasks()\n                .stream()\n                .filter(task -> task instanceof DockerBuildTask)\n                .map(Task::getPath)\n                .collect(Collectors.toList());","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/docker/DockerSupportPlugin.java#L25-L61","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (subproject build.gradle)\nplugins {\n    id('elasticsearch.docker-support')\n}\n\n// after (root build.gradle only)\nplugins {\n    id('elasticsearch.docker-support')\n}\n// subprojects access Docker info via root-registered service","handlingStrategy":"validation","validationCode":"// In a convention plugin, validate before applying\nif (project == project.getRootProject()) {\n    project.getPlugins().apply(DockerSupportPlugin.class);\n} else {\n    // access Docker service from root instead\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["gradle","build-infrastructure","plugin-configuration","docker"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}