apache/dubbo · critical · IllegalStateException

ExtensionLoader is destroyed: ${type}

Error message

ExtensionLoader is destroyed: ${type}

What it means

Thrown by ExtensionLoader.checkDestroyed() when any method on an ExtensionLoader is called after its destroy() method was invoked. ExtensionLoader instances are destroyed when their parent ExtensionDirector.destroy() is called, which cascades to all managed loaders. The message includes the SPI type so you can identify which extension loader was accessed post-destruction.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java:283

        // destroy wrapped extension instance
        for (Holder<Object> holder : cachedInstances.values()) {
            Object wrappedInstance = holder.get();
            if (wrappedInstance instanceof Disposable) {
                Disposable disposable = (Disposable) wrappedInstance;
                try {
                    disposable.destroy();
                } catch (Exception e) {
                    logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Error destroying extension " + disposable, e);
                }
            }
        }
        cachedInstances.clear();
    }

    private void checkDestroyed() {
        if (destroyed.get()) {
            throw new IllegalStateException("ExtensionLoader is destroyed: " + type);
        }
    }

    public String getExtensionName(T extensionInstance) {
        return getExtensionName(extensionInstance.getClass());
    }

    public String getExtensionName(Class<?> extensionClass) {
        getExtensionClasses(); // load class
        return cachedNames.get(extensionClass);
    }

    /**
     * This is equivalent to {@code getActivateExtension(url, key, null)}
     *
     * @param url url
     * @param key url parameter key which used to get extension point names
     * @return extension list which are activated.

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Trace the type name in the message to identify which SPI extension loader was accessed post-destroy.
  2. Find the stale reference: search for cached ExtensionLoader fields or statics that outlive the scope.
  3. Clear all cached ExtensionLoader references in your own shutdown hooks before calling dubboBootstrap.stop().
  4. Ensure the code path that triggers the late access is gated by the application/module lifecycle state.

Example fix

// before — stale cached loader accessed after shutdown
private static final ExtensionLoader<Protocol> LOADER =
    director.getExtensionLoader(Protocol.class);
// ... later after director.destroy():
LOADER.getExtension("dubbo"); // throws

// after — obtain loader fresh or check lifecycle
if (!director.isDestroyed()) { // hypothetical guard
    director.getExtensionLoader(Protocol.class).getExtension("dubbo");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ExtensionLoader has no public isDestroyed() method.
// Gate access externally by tracking the owning scope's lifecycle.
if (!scopeModelDestroyed.get()) {
    T ext = loader.getExtension(name);
}

Try / catch

try {
    T ext = loader.getExtension(name);
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("ExtensionLoader is destroyed")) {
        log.warn("ExtensionLoader for {} is destroyed — returning null", type, e);
        return null;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling any ExtensionLoader method (getExtension, getAdaptiveExtension, hasExtension, getSupportedExtensions, addExtension, replaceExtension) after the owning ExtensionDirector was destroyed. Usually a cached/stale ExtensionLoader reference survives scope teardown and is accessed by a late callback.

Common situations: A cached ExtensionLoader field or singleton is accessed during shutdown by a cleanup callback. Static initializers that capture an ExtensionLoader reference and are re-invoked during redeploy. A registry or protocol component was destroyed but a reference to its loader persists in a thread-local or cache.

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/cee54c0c80eaf966. Report an issue: GitHub.