{"record":{"id":"085eb80617ad2cae","repo":"elastic/elasticsearch","slug":"missing-classesdirs-or-classpath-property-085eb8","errorCode":null,"errorMessage":"Missing 'classesDirs' or 'classpath' property.","messagePattern":"Missing 'classesDirs' or 'classpath' property\\.","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/CheckForbiddenApisTask.java","lineNumber":560,"sourceCode":"            } catch (Exception e) {\n                throw new RuntimeException(e);\n            } finally {\n                // Close the classloader to free resources:\n                try {\n                    if (urlLoader != null) urlLoader.close();\n                } catch (IOException ioe) {\n                    // getLogger().warn(\"Cannot close classloader: \".concat(ioe.toString()));\n                }\n            }\n        }\n\n        private void writeMarker(File successMarker) throws IOException {\n            Files.write(successMarker.toPath(), new byte[] {}, StandardOpenOption.CREATE);\n        }\n\n        private URLClassLoader createClassLoader(FileCollection classpath, FileCollection classesDirs) {\n            if (classesDirs == null || classpath == null) {\n                throw new InvalidUserDataException(\"Missing 'classesDirs' or 'classpath' property.\");\n            }\n\n            final Set<File> cpElements = new LinkedHashSet<>();\n            cpElements.addAll(classpath.getFiles());\n            cpElements.addAll(classesDirs.getFiles());\n            final URL[] urls = new URL[cpElements.size()];\n            try {\n                int i = 0;\n                for (final File cpElement : cpElements) {\n                    urls[i++] = cpElement.toURI().toURL();\n                }\n                assert i == urls.length;\n            } catch (MalformedURLException mfue) {\n                throw new InvalidUserDataException(\"Failed to build classpath URLs.\", mfue);\n            }\n\n            RegularFileProperty foreignApiJarProp = getParameters().getForeignApiJar();\n            if (foreignApiJarProp.isPresent()) {","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/CheckForbiddenApisTask.java#L542-L578","documentation":"Thrown by CheckForbiddenApisTask.createClassLoader(FileCollection classpath, FileCollection classesDirs) when either classesDirs or classpath is null. The forbidden-apis task needs both — class files to check and a classpath to resolve them against — and refuses to build the loader otherwise. Configuration-time guard analogous to [241] but for this task.","triggerScenarios":"A CheckForbiddenApisTask registration where classFiles/classesDirs or classpath was never assigned, so the createClassLoader call at execution receives a null.","commonSituations":"Custom task registration missing the wiring; a source set rename that broke the property assignment; disabling a source set but keeping its forbidden-apis task.","solutions":["Locate the task registration and set both classesDirs and classpath, e.g. from the matching source set.","Wire classesDirs = sourceSets.main.output.classesDirs and classpath = sourceSets.main.compileClasspath.","If the task is unused for that source set, delete the registration."],"exampleFix":"// before\ntasks.named('forbiddenApisMain', CheckForbiddenApisTask) {\n  // missing wiring\n}\n\n// after\ntasks.named('forbiddenApisMain', CheckForbiddenApisTask) {\n  classesDirs = sourceSets.main.output.classesDirs\n  classpath = sourceSets.main.compileClasspath\n}","handlingStrategy":"validation","validationCode":"// Assert both properties are wired at configuration time\ntasks.named('forbiddenApisMain').configure(t -> {\n    CheckForbiddenApisTask task = (CheckForbiddenApisTask) t;\n    if (task.getClassesDirs() == null || task.getClasspath() == null) {\n        throw new InvalidUserDataException(\"forbiddenApisMain needs classesDirs and classpath\");\n    }\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always wire classesDirs and classpath from the source set when registering the task.","When renaming a source set, update all forbidden-apis task registrations.","Remove forbidden-apis tasks for source sets you have deleted."],"tags":["gradle","forbidden-apis","precommit","task-config","classpath"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}