{"record":{"id":"cec26004731910b1","repo":"apache/dubbo","slug":"expect-only-one-but-found-size-matched-construc","errorCode":null,"errorMessage":"Expect only one but found ${size} matched constructors for type: ${type}, matched constructors: ${matchedConstructors}","messagePattern":"Expect only one but found (.+?) matched constructors for type: (.+?), matched constructors: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java","lineNumber":77,"sourceCode":"        List<Constructor<?>> matchedConstructors = new ArrayList<>();\n        Constructor<?>[] declaredConstructors = type.getConstructors();\n        for (Constructor<?> constructor : declaredConstructors) {\n            if (isMatched(constructor)) {\n                matchedConstructors.add(constructor);\n            }\n        }\n        // remove default constructor from matchedConstructors\n        if (defaultConstructor != null) {\n            matchedConstructors.remove(defaultConstructor);\n        }\n\n        // match order:\n        // 1. the only matched constructor with parameters\n        // 2. default constructor if absent\n\n        Constructor<?> targetConstructor;\n        if (matchedConstructors.size() > 1) {\n            throw new IllegalArgumentException(\"Expect only one but found \" + matchedConstructors.size()\n                    + \" matched constructors for type: \" + type.getName() + \", matched constructors: \"\n                    + matchedConstructors);\n        } else if (matchedConstructors.size() == 1) {\n            targetConstructor = matchedConstructors.get(0);\n        } else if (defaultConstructor != null) {\n            targetConstructor = defaultConstructor;\n        } else {\n            throw new IllegalArgumentException(\"None matched constructor was found for type: \" + type.getName());\n        }\n\n        // create instance with arguments\n        Class<?>[] parameterTypes = targetConstructor.getParameterTypes();\n        Object[] args = new Object[parameterTypes.length];\n        for (int i = 0; i < parameterTypes.length; i++) {\n            args[i] = getArgumentValueForType(parameterTypes[i]);\n        }\n        return (T) targetConstructor.newInstance(args);\n    }","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/beans/support/InstantiationStrategy.java#L59-L95","documentation":"Thrown by InstantiationStrategy.instantiate when more than one public constructor matches Dubbo's rule (all parameters assignable to ScopeModel subclasses, after removing the default constructor). Dubbo picks a single matched constructor to inject scope models into; multiple candidates are ambiguous, so it refuses to guess and lists them. IllegalArgumentException, surfaced wrapped as ScopeBeanException by callers.","triggerScenarios":"A class instantiated via InstantiationStrategy (SPI extension or registered bean) that has two or more public constructors whose parameters are all ScopeModel/ApplicationModel/FrameworkModel/ModuleModel types. E.g. constructors (ApplicationModel) and (FrameworkModel) both pass isMatched.","commonSituations":"Extension classes written with multiple scope-model constructors; refactoring that added a second constructor taking a different ScopeModel subclass; SPI implementation upgraded to support multiple model types simultaneously.","solutions":["Keep exactly one public constructor that takes ScopeModel arguments (consolidate onto ScopeModel or ApplicationModel).","If two constructors are genuinely needed, make all but one non-public or parameterized by non-ScopeModel types so only one matches isMatched.","Prefer a single (ScopeModel) constructor and obtain specific models via scopeModelAccessor inside the bean.","As a fallback, register an explicit Supplier/instance so InstantiationStrategy.instantiate is bypassed."],"exampleFix":"// before\npublic class MyExt {\n    public MyExt(ApplicationModel app) { ... }\n    public MyExt(FrameworkModel fw) { ... }  // both match -> ambiguous\n}\n// after\npublic class MyExt {\n    public MyExt(ScopeModel scope) {\n        this.app = scope.getDefaultAppModel();\n    }\n}","handlingStrategy":"validation","validationCode":"// Ensure at most one ScopeModel-arg public constructor exists\nboolean singleScopeConstructor(Class<?> c) {\n    int matched = 0;\n    Constructor<?> def = null;\n    try { def = c.getConstructor(); } catch (NoSuchMethodException ignored) {}\n    for (Constructor<?> ctor : c.getConstructors()) {\n        if (ctor.equals(def)) continue;\n        boolean ok = true;\n        for (Class<?> p : ctor.getParameterTypes())\n            ok &= org.apache.dubbo.rpc.model.ScopeModel.class.isAssignableFrom(p);\n        if (ok) matched++;\n    }\n    return matched <= 1;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Design extension classes with at most one public ScopeModel-arg constructor.","If multiple models are needed, accept the base ScopeModel and derive the rest from it.","If you must keep several constructors, make all but one non-public or non-matching.","Register an explicit Supplier/instance to bypass reflective constructor matching."],"tags":["instantiation","constructor","reflection","dubbo-common"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}