elastic/elasticsearch · error · InvalidUserDataException
Missing 'classesDirs' or 'classpath' property.
Error message
Missing 'classesDirs' or 'classpath' property.
What it means
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.
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/CheckForbiddenApisTask.java:560
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// Close the classloader to free resources:
try {
if (urlLoader != null) urlLoader.close();
} catch (IOException ioe) {
// getLogger().warn("Cannot close classloader: ".concat(ioe.toString()));
}
}
}
private void writeMarker(File successMarker) throws IOException {
Files.write(successMarker.toPath(), new byte[] {}, StandardOpenOption.CREATE);
}
private URLClassLoader createClassLoader(FileCollection classpath, FileCollection classesDirs) {
if (classesDirs == null || classpath == null) {
throw new InvalidUserDataException("Missing 'classesDirs' or 'classpath' property.");
}
final Set<File> cpElements = new LinkedHashSet<>();
cpElements.addAll(classpath.getFiles());
cpElements.addAll(classesDirs.getFiles());
final URL[] urls = new URL[cpElements.size()];
try {
int i = 0;
for (final File cpElement : cpElements) {
urls[i++] = cpElement.toURI().toURL();
}
assert i == urls.length;
} catch (MalformedURLException mfue) {
throw new InvalidUserDataException("Failed to build classpath URLs.", mfue);
}
RegularFileProperty foreignApiJarProp = getParameters().getForeignApiJar();
if (foreignApiJarProp.isPresent()) {View on GitHub (pinned to db6a809a66)
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.
Example fix
// before
tasks.named('forbiddenApisMain', CheckForbiddenApisTask) {
// missing wiring
}
// after
tasks.named('forbiddenApisMain', CheckForbiddenApisTask) {
classesDirs = sourceSets.main.output.classesDirs
classpath = sourceSets.main.compileClasspath
} Defensive patterns
Strategy: validation
Validate before calling
// Assert both properties are wired at configuration time
tasks.named('forbiddenApisMain').configure(t -> {
CheckForbiddenApisTask task = (CheckForbiddenApisTask) t;
if (task.getClassesDirs() == null || task.getClasspath() == null) {
throw new InvalidUserDataException("forbiddenApisMain needs classesDirs and classpath");
}
}); Prevention
- 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.
When it happens
Trigger: A CheckForbiddenApisTask registration where classFiles/classesDirs or classpath was never assigned, so the createClassLoader call at execution receives a null.
Common situations: 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.
Related errors
- No signatures were added to task; use properties 'signatures
- Failed to build classpath URLs.
- IO problem while reading files with API signatures.
- Parsing signatures failed: {}
- Failed to load one of the given class files.
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/085eb80617ad2cae.
Report an issue: GitHub.