{"record":{"id":"fbeb66299df5d8bb","repo":"apache/dubbo","slug":"extension-type-type-is-not-an-extension-beca","errorCode":null,"errorMessage":"Extension type (${type}) is not an extension, because it is NOT annotated by @SPI!","messagePattern":"Extension type \\((.+?)\\) is not an extension, because it is NOT annotated by @SPI!","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java","lineNumber":76,"sourceCode":"    }\n\n    @Override\n    public ExtensionDirector getExtensionDirector() {\n        return this;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {\n        checkDestroyed();\n        if (type == null) {\n            throw new IllegalArgumentException(\"Extension type == null\");\n        }\n        if (!type.isInterface()) {\n            throw new IllegalArgumentException(\"Extension type (\" + type + \") is not an interface!\");\n        }\n        if (!withExtensionAnnotation(type)) {\n            throw new IllegalArgumentException(\"Extension type (\" + type\n                    + \") is not an extension, because it is NOT annotated with @\" + SPI.class.getSimpleName() + \"!\");\n        }\n\n        // 1. find in local cache\n        ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);\n\n        ExtensionScope scope = extensionScopeMap.get(type);\n        if (scope == null) {\n            SPI annotation = type.getAnnotation(SPI.class);\n            scope = annotation.scope();\n            extensionScopeMap.put(type, scope);\n        }\n\n        if (loader == null && scope == ExtensionScope.SELF) {\n            // create an instance in self scope\n            loader = createExtensionLoader0(type);\n        }\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java#L58-L94","documentation":"Thrown by ExtensionDirector.getExtensionLoader(Class<T> type) when the type IS an interface but is NOT annotated with @SPI. Dubbo's extension system only manages interfaces explicitly marked with @SPI — this annotation declares the extension point and optionally specifies a default implementation. Without it, Dubbo refuses to treat the interface as an extension point.","triggerScenarios":"Calling getExtensionLoader() with a plain Java interface that lacks the @SPI annotation. For example, passing a standard library interface (like java.lang.Runnable) or a custom interface you forgot to annotate. Also triggered when a custom interface was previously annotated but the annotation was removed during refactoring.","commonSituations":"Developer creates a custom SPI interface but forgets @SPI annotation. The @SPI annotation import was removed during IDE auto-import cleanup. Migrating from a framework where SPI registration works differently (e.g., Java ServiceLoader without annotations).","solutions":["Add the @SPI annotation to the interface: @SPI(\"defaultImplName\") public interface MyExtension { ... }.","Import org.apache.dubbo.common.extension.SPI to resolve the annotation.","If using a third-party interface that you cannot annotate, wrap it in your own @SPI-annotated interface."],"exampleFix":"// before — missing @SPI annotation\npublic interface MyLoadBalance {\n    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation);\n}\n\n// after — annotate with @SPI\n@SPI(\"random\")\npublic interface MyLoadBalance {\n    <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation);\n}","handlingStrategy":"validation","validationCode":"if (!type.isAnnotationPresent(SPI.class)) {\n    throw new IllegalArgumentException(\n        type + \" must be annotated with @SPI to be used as a Dubbo extension point\");\n}\nExtensionLoader<T> loader = director.getExtensionLoader(type);","typeGuard":"static <T> boolean hasSpiAnnotation(Class<T> type) {\n    return type != null && type.isInterface() && type.isAnnotationPresent(SPI.class);\n}","tryCatchPattern":"try {\n    ExtensionLoader<T> loader = director.getExtensionLoader(type);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"NOT annotated with @SPI\")) {\n        log.error(\"Interface lacks @SPI annotation — add it to declare the extension point\", e);\n    }\n    throw e;\n}","preventionTips":["When creating a custom SPI extension point, always add @SPI(\"defaultName\") on the interface.","Ensure the SPI import (org.apache.dubbo.common.extension.SPI) survives IDE auto-import cleanup.","Use a code template or Archetype for new SPI interfaces that includes the @SPI annotation."],"tags":["dubbo-spi","annotation","extension-system","configuration"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}