apache/dubbo · error · IllegalStateException
No such constructor "public…
Error message
No such constructor "public <localClass.getSimpleName()>(<interfaceClass.getName()>)" in local implementation class <localClass.getName()>
What it means
Thrown by AbstractInterfaceConfig.verify() when a local implementation class (set via the local/stub attribute) does not expose a public constructor that takes the service interface as its sole parameter. Dubbo's local stub mechanism requires this constructor so the framework can instantiate the stub and inject a reference to the remote interface for client-side logic execution. The verify() method first confirms the local class implements the interface, then uses ReflectUtils.findConstructor() to locate the required constructor signature, throwing IllegalStateException if it cannot.
Solutions
- Add a public constructor to the local implementation class whose single parameter is the service interface type, e.g. `public MyLocalImpl(MyService service) { ... }`.
- Verify the local class actually implements the declared interface (the preceding isAssignableFrom check must also pass).
- Ensure the constructor is public and not generic/erased in a way that prevents reflective lookup.
Example fix
// before
public class MyLocalImpl implements MyService {
public MyLocalImpl() { }
}
// after
public class MyLocalImpl implements MyService {
private final MyService remote;
public MyLocalImpl(MyService remote) { this.remote = remote; }
} Defensive patterns
Strategy: validation
Validate before calling
// Before setting local/stub, verify the constructor exists
import org.apache.dubbo.common.utils.ReflectUtils;
Class<?> iface = MyService.class;
Class<?> local = MyLocalImpl.class;
if (!iface.isAssignableFrom(local)) {
throw new IllegalStateException("local class does not implement interface");
}
try {
ReflectUtils.findConstructor(local, iface);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Missing public constructor " + local.getSimpleName() + "(" + iface.getName() + ")", e);
}
serviceConfig.setLocal(local.getName()); Type guard
static boolean hasDubboStubConstructor(Class<?> iface, Class<?> local) {
if (!iface.isAssignableFrom(local)) return false;
try {
org.apache.dubbo.common.utils.ReflectUtils.findConstructor(local, iface);
return true;
} catch (NoSuchMethodException e) {
return false;
}
} Try / catch
try {
serviceConfig.setLocal(MyLocalImpl.class.getName());
serviceConfig.export();
} catch (IllegalStateException e) {
if (e.getMessage().contains("No such constructor")) {
// add the required public Constructor(iface) to the stub class
} else throw e;
} Prevention
- Always add a public constructor taking the service interface to any local/stub class.
- Run a unit test that reflectively checks the stub constructor before wiring.
- Keep stub classes small and dedicated to one interface.
When it happens
Trigger: Calling ServiceConfig.setLocal() or configuring <dubbo:service local="..."> / stub="..." with a class that lacks a `public LocalClass(InterfaceType)` constructor. Also triggered by the local stub SPI when the stub class only has a default no-arg constructor or a constructor with mismatched parameter types.
Common situations: Writing a custom local stub for client-side caching/validation logic but forgetting to add the constructor Dubbo mandates. Copying a stub class from another interface without updating the constructor parameter type. Using an older stub pattern (no-arg init) incompatible with Dubbo's required signature.
Related errors
- Append parameters failed
- Can not merge result because missing method
- Cannot assign nested class when refreshing config
- cannot find field ,field is null
- create config instance failed, id
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/4b336bcab996fad4.
Report an issue: GitHub.
Appendix: source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/config/AbstractInterfaceConfig.java:493
if (ConfigUtils.isNotEmpty(className)) {
Class<?> localClass = ConfigUtils.isDefault(className)
? ReflectUtils.forName(interfaceClass.getName() + label)
: ReflectUtils.forName(className);
verify(interfaceClass, localClass);
}
}
private void verify(Class<?> interfaceClass, Class<?> localClass) {
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName()
+ " not implement interface " + interfaceClass.getName());
}
try {
// Check if the localClass a constructor with parameter whose type is interfaceClass
ReflectUtils.findConstructor(localClass, interfaceClass);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "("
+ interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
}
}
private void convertRegistryIdsToRegistries() {
computeValidRegistryIds();
if (StringUtils.isEmpty(registryIds)) {
if (CollectionUtils.isEmpty(registries)) {
List<RegistryConfig> registryConfigs = getConfigManager().getDefaultRegistries();
registryConfigs = new ArrayList<>(registryConfigs);
setRegistries(registryConfigs);
}
} else {
String[] ids = COMMA_SPLIT_PATTERN.split(registryIds);
List<RegistryConfig> tmpRegistries = new ArrayList<>();
Arrays.stream(ids).forEach(id -> {
if (tmpRegistries.stream().noneMatch(reg -> reg.getId().equals(id))) {
Optional<RegistryConfig> globalRegistry = getConfigManager().getRegistry(id);View on GitHub (pinned to 3a3043227f)