elastic/elasticsearch · error · RuntimeException

Named component {}({}) does not extend from an extensible cl

Error message

Named component {}({}) does not extend from an extensible class

What it means

Thrown at build time by NamedComponentScanner.scanForNamedClasses when a class annotated @NamedComponent is not itself (or via its hierarchy) annotated @Extensible / does not resolve to an extensible base class in the extensibleClassScanner's findings. The scanner cross-references the two ASM scans; a @NamedComponent with no matching @Extensible entry is a contract violation. RuntimeException aborting the build/plugin descriptor generation.

Source

Thrown at libs/plugin-scanner/src/main/java/org/elasticsearch/plugin/scanner/NamedComponentScanner.java:88

            (classname, map) -> new AnnotationVisitor(Opcodes.ASM9) {
                @Override
                public void visit(String key, Object value) {
                    assert key.equals("value");
                    assert value instanceof String;
                    map.put(value.toString(), classname);
                }
            }
        );

        namedComponentsScanner.visit(classReaders);

        Map<String, Map<String, String>> componentInfo = new HashMap<>();
        for (var e : namedComponentsScanner.getFoundClasses().entrySet()) {
            String name = e.getKey();
            String classnameWithSlashes = e.getValue();
            String extensibleClassnameWithSlashes = extensibleClassScanner.getFoundClasses().get(classnameWithSlashes);
            if (extensibleClassnameWithSlashes == null) {
                throw new RuntimeException(
                    "Named component " + name + "(" + pathToClassName(classnameWithSlashes) + ") does not extend from an extensible class"
                );
            }
            var named = componentInfo.computeIfAbsent(pathToClassName(extensibleClassnameWithSlashes), k -> new HashMap<>());
            named.put(name, pathToClassName(classnameWithSlashes));
        }
        return componentInfo;
    }

    private static String pathToClassName(String classWithSlashes) {
        return classWithSlashes.replace('/', '.');
    }

}

View on GitHub (pinned to db6a809a66)

Solutions

  1. Annotate the extension-point interface or abstract class the named component extends/implements with @org.elasticsearch.plugin.Extensible.
  2. Ensure the extensible base is on the compile/scanner classpath (the ASM ClassReaders must visit it).
  3. Rebuild the plugin descriptor (re-run the gradle scanner task) after adding @Extensible so the cross-reference resolves.
  4. If the named component genuinely has no extensible base, remove @NamedComponent — it is not a valid extension point.

Example fix

// before
@NamedComponent("my_thing")
public class MyThing implements Thing { ... }  // Thing not @Extensible

// after
@Extensible
public interface Thing { ... }

@NamedComponent("my_thing")
public class MyThing implements Thing { ... }
Defensive patterns

Strategy: validation

Validate before calling

// At build time, before scanning, assert every @NamedComponent has an @Extensible base.
for (Class<?> c : namedComponentClasses) {
    boolean hasExtensibleBase = Arrays.stream(c.getInterfaces())
        .flatMap(i -> Stream.concat(Stream.of(i), Arrays.stream(i.getInterfaces())))
        .anyMatch(i -> i.isAnnotationPresent(Extensible.class));
    if (!hasExtensibleBase) {
        throw new IllegalStateException(c + " is @NamedComponent but extends no @Extensible type");
    }
}

Prevention

When it happens

Trigger: Annotating a class with @NamedComponent(value="foo") but forgetting @Extensible on the base interface/superclass it extends; renaming/moving the extensible base so ASM no longer records it; an inheritance chain where @Extensible is on an interface that the named component does not transitively implement (according to the class files on the scanner's classpath).

Common situations: Plugin authors adding a new named component for the first time and missing the @Extensible declaration on the extension point; refactoring that splits an interface without re-annotating; a build that runs the scanner with an incomplete classpath so the extensible base is never visited.

Related errors


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