elastic/elasticsearch · error · RuntimeException

Failed to load class

Error message

Failed to load class {name}. Incorrect classpath?

What it means

Thrown by TestingConventionsCheckTask.loadClassWithoutInitializing when Class.forName(name, false, classLoader) raises ClassNotFoundException. The visitor collected a .class file's fully-qualified name but the configured classloader cannot resolve that class, indicating the classpath feeding the check is incomplete or inconsistent with the compiled output being scanned.

Solutions

  1. Confirm the task's classloader classpath includes the compiled class roots being scanned (project.output + test dependencies).
  2. Clean and rebuild the affected project (./gradlew :<project>:clean :<project>:compileTestJava) to remove stale .class files.
  3. If a referenced class was deleted from source but a stale .class remains, a clean removes it; if it's a legitimate external class, add the missing dependency.
  4. Re-run :<project>:testingConventions with --info to see the failing class name, then verify it exists in source.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure scanned class roots are on the classloader's classpath
List<File> roots = compiledOutputRoots;
URLClassLoader cl = new URLClassLoader(toUrls(roots), parent);
for (String fqcn : visitor.fullQualifiedClassNames) {
    if (cl.getResource(fqcn.replace('.', '/') + ".class") == null) {
        throw new IllegalStateException("Scanned class not on classpath: " + fqcn);
    }
}

Try / catch

try { Class<?> c = loadClassWithoutInitializing(name, cl); }
catch (RuntimeException e) {
    if (e.getCause() instanceof ClassNotFoundException) { /* fix classpath, do not ignore */ }
    throw e;
}

Prevention

When it happens

Trigger: The ClassLoadingFileVisitor enumerates .class files under compiled output and the task then attempts Class.forName for each; if the classloader was built from a classpath that omits the directory actually being scanned (or a parent/sibling output it depends on), ClassNotFoundException is raised.

Common situations: The convention check's classpath does not include the project's own compiled output dir; incremental/parallel build produced a stale class file whose source was removed; cross-project test fixtures referenced but not on the classpath; misconfigured custom SourceSet whose output isn't wired into the check.

Related errors


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

Appendix: source

Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/precommit/TestingConventionsCheckTask.java:261

                && Modifier.isStatic(method.getModifiers()) == false
                && method.getReturnType().equals(Void.TYPE);
        }

        private static boolean isAnnotated(Method method, Class<?> annotation) {
            return Stream.of(method.getAnnotations())
                .anyMatch(presentAnnotation -> annotation.isAssignableFrom(presentAnnotation.getClass()));
        }

        private static Class<?> loadClassWithoutInitializing(String name, ClassLoader classLoader) {
            try {
                return Class.forName(
                    name,
                    // Don't initialize the class to save time. Not needed for this test and this doesn't share a VM with any other tests.
                    false,
                    classLoader
                );
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("Failed to load class " + name + ". Incorrect classpath?", e);
            }
        }
    }

    private static final class ClassLoadingFileVisitor extends EmptyFileVisitor {
        private static final String CLASS_POSTFIX = ".class";
        private List<String> fullQualifiedClassNames = new ArrayList<>();

        @Override
        public void visitFile(FileVisitDetails fileVisitDetails) {
            String fileName = fileVisitDetails.getName();
            if (fileName.endsWith(CLASS_POSTFIX)) {
                String packageName = Arrays.stream(fileVisitDetails.getRelativePath().getSegments())
                    .takeWhile(s -> s.equals(fileName) == false)
                    .collect(Collectors.joining("."));
                String simpleClassName = fileName.replace(CLASS_POSTFIX, "");
                String fullQualifiedClassName = packageName + (packageName.isEmpty() ? "" : ".") + simpleClassName;
                fullQualifiedClassNames.add(fullQualifiedClassName);

View on GitHub (pinned to db6a809a66)