apache/dubbo · error · IllegalStateException
No such constructor "public <localClass.getSimpleName()>(<in
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.
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)
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
- Can not merge result because missing method [ {merger} ] in
- Expect only one but found ${size} matched constructors for t
- None matched constructor was found for type: ${type}
- cannot find field %s,field is null
- Append parameters failed: <message>
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/4b336bcab996fad4.
Report an issue: GitHub.