alibaba/COLA · error · ExtensionException

EXTENSION_DEFINE_DUPLICATE

EXTENSION_DEFINE_DUPLICATE

Error message

Duplicate registration is not allowed for :${extensionCoordinate}

What it means

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.

Source

Thrown at cola-components/cola-component-extension-starter/src/main/java/com/alibaba/cola/extension/register/ExtensionRegister.java:57

    @Resource
    private ExtensionRepository extensionRepository;

    public final static String EXTENSION_EXTPT_NAMING = "ExtPt";


    public void doRegistration(ExtensionPointI extensionObject) {
        Class<?> extensionClz = extensionObject.getClass();
        if (AopUtils.isAopProxy(extensionObject)) {
            extensionClz = AopUtils.getTargetClass(extensionObject);
        }
        Extension extensionAnn = AnnotatedElementUtils.findMergedAnnotation(extensionClz, Extension.class);
        BizScenario bizScenario = BizScenario.valueOf(extensionAnn.bizId(), extensionAnn.useCase(), extensionAnn.scenario());
        ExtensionCoordinate extensionCoordinate = new ExtensionCoordinate(calculateExtensionPoint(extensionClz), bizScenario.getUniqueIdentity());
        ExtensionPointI preVal = extensionRepository.getExtensionRepo().put(extensionCoordinate, extensionObject);
        if (preVal != null) {
            String errMessage = "Duplicate registration is not allowed for :" + extensionCoordinate;
            throw new ExtensionException(EXTENSION_DEFINE_DUPLICATE, errMessage);
        }
    }

    public void doRegistrationExtensions(ExtensionPointI extensionObject){
        Class<?> extensionClz = extensionObject.getClass();
        if (AopUtils.isAopProxy(extensionObject)) {
            extensionClz = ClassUtils.getUserClass(extensionObject);
        }

        Extensions extensionsAnnotation = AnnotationUtils.findAnnotation(extensionClz, Extensions.class);

        //Support multiple extensions registration
        String[] bizIds = extensionsAnnotation.bizId();
        String[] useCases = extensionsAnnotation.useCase();
        String[] scenarios = extensionsAnnotation.scenario();
        for (String bizId : bizIds) {
            for (String useCase : useCases) {
                for (String scenario : scenarios) {

View on GitHub (pinned to 352e1a8675)

Solutions

  1. 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.
  2. Remove the redundant bean or narrow its component-scan/package so it registers once.
  3. If behavior should be replaced, delete the old extension implementation rather than adding a new one with identical identity.
  4. Use distinct scenarios via doRegistrationExtensions (@Extension with a scenario list) when one class legitimately serves multiple scenarios — but ensure no overlap with other classes.

Example fix

// before (two classes both declare this)
@Extension(bizId = "trade", useCase = "refund", scenario = "normal")
// after — make the second implementation's identity unique
@Extension(bizId = "trade", useCase = "refund", scenario = "vip")
Defensive patterns

Strategy: validation

Validate before calling

// startup self-check
Set<String> seen = new HashSet<>();
for (Object ext : extensions) {
    Extension a = AnnotatedElementUtils.findMergedAnnotation(ext.getClass(), Extension.class);
    if (!seen.add(a.bizId() + "|" + a.useCase() + "|" + a.scenario())) { throw new IllegalStateException("Duplicate @Extension identity"); }
}

Try / catch

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; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of alibaba/COLA@352e1a8675 (2026-09-08). Data as JSON: /api/errors/2105ce2d27c4cfd9. Report an issue: GitHub.