{"record":{"id":"2105ce2d27c4cfd9","repo":"alibaba/COLA","slug":"extension-define-duplicate","errorCode":"EXTENSION_DEFINE_DUPLICATE","errorMessage":"Duplicate registration is not allowed for :${extensionCoordinate}","messagePattern":"Duplicate registration is not allowed for :(.+?)","errorType":"error_code","errorClass":"ExtensionException","httpStatus":null,"severity":"error","filePath":"cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/ExtensionRegister.java","lineNumber":57,"sourceCode":"\n    @Resource\n    private ExtensionRepository extensionRepository;\n\n    public final static String EXTENSION_EXTPT_NAMING = \"ExtPt\";\n\n\n    public void doRegistration(ExtensionPointI extensionObject) {\n        Class<?> extensionClz = extensionObject.getClass();\n        if (AopUtils.isAopProxy(extensionObject)) {\n            extensionClz = AopUtils.getTargetClass(extensionObject);\n        }\n        Extension extensionAnn = AnnotatedElementUtils.findMergedAnnotation(extensionClz, Extension.class);\n        BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario());\n        ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(calculateExtensionPoint(extensionClz), bizScenario.getUniqueIdentity());\n        ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extensionObject);\n        if (preVal != null) {\n            String errMessage = \"Duplicate registration is not allowed for :\" + extensionCoordinate;\n            throw new ExtensionException(EXTENSION_DEFINE_DUPLICATE, errMessage);\n        }\n    }\n\n    public void doRegistrationExtensions(ExtensionPointI extensionObject){\n        Class<?> extensionClz = extensionObject.getClass();\n        if (AopUtils.isAopProxy(extensionObject)) {\n            extensionClz = ClassUtils.getUserClass(extensionObject);\n        }\n\n        Extensions extensionsAnnotation = AnnotationUtils.findAnnotation(extensionClz, Extensions.class);\n\n        //Support multiple extensions registration\n        String[] bizIds = extensionsAnnotation.bizId();\n        String[] useCases = extensionsAnnotation.useCase();\n        String[] scenarios = extensionsAnnotation.scenario();\n        for (String bizId : bizIds) {\n            for (String useCase : useCases) {\n                for (String scenario : scenarios) {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/alibaba/COLA/blob/352e1a867538d9cd40e1b11e4028fa652fc97557/cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/ExtensionRegister.java#L39-L75","documentation":"ExtensionRegister.doRegistration puts each @Extension implementation into the extension repository keyed by an ExtensionCoordinate (extension point interface + BizScenario identity). If put() returns a previous value, an extension is already registered for that exact coordinate, and ExtensionException with code EXTENSION_DEFINE_DUPLICATE is thrown to protect against silently overwriting behavior. One business coordinate must map to exactly one implementation.","triggerScenarios":"Spring startup/registration of two beans annotated @Extension with the same bizId/useCase/scenario for the same extension point interface — e.g. copy-pasted extension classes, or the same class registered twice (multiple bean definitions, double component scan).","commonSituations":"Copy-pasting an extension class without changing @Extension(bizId/useCase/scenario); two modules in a multi-module project both defining an extension for the same scenario; duplicate @Component scanning of the same package.","solutions":["Find the two @Extension classes claiming the same coordinate (the message names the ExtensionCoordinate) and change one's bizId/useCase/scenario to a distinct value.","Remove the redundant bean or narrow its component-scan/package so it registers once.","If behavior should be replaced, delete the old extension implementation rather than adding a new one with identical identity.","Use distinct scenarios via doRegistrationExtensions (@Extension with a scenario list) when one class legitimately serves multiple scenarios — but ensure no overlap with other classes."],"exampleFix":"// before (two classes both declare this)\n@Extension(bizId = \"trade\", useCase = \"refund\", scenario = \"normal\")\n// after — make the second implementation's identity unique\n@Extension(bizId = \"trade\", useCase = \"refund\", scenario = \"vip\")","handlingStrategy":"validation","validationCode":"// startup self-check\nSet<String> seen = new HashSet<>();\nfor (Object ext : extensions) {\n    Extension a = AnnotatedElementUtils.findMergedAnnotation(ext.getClass(), Extension.class);\n    if (!seen.add(a.bizId() + \"|\" + a.useCase() + \"|\" + a.scenario())) { throw new IllegalStateException(\"Duplicate @Extension identity\"); }\n}","typeGuard":null,"tryCatchPattern":"try { applicationContext.register(...); context.refresh(); } catch (ExtensionException e) { if (\"EXTENSION_DEFINE_DUPLICATE\".equals(e.getErrCode())) { log.error(e.getMessage()); /* fail startup with actionable message */ } throw e; }","preventionTips":["Keep @Extension identities in a registry of constants to avoid copy-paste collisions","One team owns each bizId.useCase namespace","Add a unit test scanning @Extension annotations for uniqueness","Avoid registering the same class through multiple bean-definition paths"],"tags":["java","cola","duplicate-registration","extension-point","spring"],"backgroundTag":"conflicting-config-options","analyzedSha":"352e1a867538d9cd40e1b11e4028fa652fc97557","analyzedAt":"2026-09-08T00:46:23.972Z","contentChangedAt":"2026-09-08T00:46:23.972Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}