apache/dubbo · error · IllegalArgumentException
SPI annotation not found for class: ${type.getName()}
Error message
SPI annotation not found for class: ${type.getName()} What it means
ScopeModelUtil.getDefaultScopeModel() reads the @SPI annotation on the given type to determine which scope (FRAMEWORK/APPLICATION/MODULE) the default model should be. If the type is not annotated with @SPI, there is no scope metadata to read and Dubbo refuses to guess — the class is not a Dubbo extension point. The same check exists in getExtensionLoader() (error 372).
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ScopeModelUtil.java:34
*/
package org.apache.dubbo.rpc.model;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.extension.SPI;
public class ScopeModelUtil {
public static <T> ScopeModel getOrDefault(ScopeModel scopeModel, Class<T> type) {
if (scopeModel != null) {
return scopeModel;
}
return getDefaultScopeModel(type);
}
private static <T> ScopeModel getDefaultScopeModel(Class<T> type) {
SPI spi = type.getAnnotation(SPI.class);
if (spi == null) {
throw new IllegalArgumentException("SPI annotation not found for class: " + type.getName());
}
switch (spi.scope()) {
case FRAMEWORK:
return FrameworkModel.defaultModel();
case APPLICATION:
return ApplicationModel.defaultModel();
case MODULE:
return ApplicationModel.defaultModel().getDefaultModule();
default:
throw new IllegalStateException("Unable to get default scope model for type: " + type.getName());
}
}
public static ModuleModel getModuleModel(ScopeModel scopeModel) {
if (scopeModel == null) {
return ApplicationModel.defaultModel().getDefaultModule();
}
if (scopeModel instanceof ModuleModel) {View on GitHub (pinned to 3a3043227f)
Solutions
- Pass an actual Dubbo SPI extension type — a class/interface annotated with @SPI (e.g. Protocol.class, Serialization.class).
- If this is your own extension point, add `@SPI` (with a default value) to the interface.
- Avoid calling ScopeModelUtil.getOrDefault with null scopeModel unless you are certain the type carries @SPI; instead pass an explicit ScopeModel.
- Double-check the class import — make sure you are passing the Dubbo SPI interface, not its impl class or a config class.
Example fix
// before (throws: HelloService has no @SPI) ScopeModel sm = ScopeModelUtil.getOrDefault(null, HelloService.class); // after (pass an explicit scope, or a real @SPI type) ScopeModel sm = ScopeModelUtil.getOrDefault(applicationModel, HelloService.class); // or, for an extension: ScopeModel sm = ScopeModelUtil.getOrDefault(null, Protocol.class); // Protocol is @SPI
Defensive patterns
Strategy: validation
Validate before calling
if (type.getAnnotation(SPI.class) == null) {
throw new IllegalArgumentException("Not an @SPI type: " + type.getName()
+ " — pass a Dubbo extension interface or an explicit ScopeModel");
}
ScopeModel sm = ScopeModelUtil.getOrDefault(scopeModel, type); Type guard
static boolean isSpiType(Class<?> type) {
return type != null && type.isInterface() && type.getAnnotation(SPI.class) != null;
} Prevention
- Only pass @SPI-annotated interfaces to scope-default resolution.
- When in doubt, pass an explicit ScopeModel so the SPI metadata is not consulted.
- Annotate your own extension interfaces with @SPI.
When it happens
Trigger: Calling ScopeModelUtil.getOrDefault(null, SomeClass.class) or indirectly via a path that resolves the default scope model, where SomeClass is not annotated with @org.apache.dubbo.common.extension.SPI. Common when an internal helper resolves a default model for a plain interface or a class that should have been an SPI but isn't.
Common situations: Passing a non-extension interface (a plain business interface or a config class) where an SPI extension type is expected. Custom integrations that wrap Dubbo SPI resolution and forward arbitrary classes. Forgetting the @SPI annotation on a hand-written extension interface. Version mismatches where an interface lost its @SPI annotation.
Related errors
- ExtensionLoader for [${type}] is not found
- Unable to get default scope model for type: ${type.getName()
- INTERNAL_ERROR
- There is no merger to merge result.
- register bean failed! name=${name}, type=${className}
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/40ba7ac024a7b959.
Report an issue: GitHub.