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
- Trace the type name in the message to identify which SPI extension loader was accessed post-destroy.
- Find the stale reference: search for cached ExtensionLoader fields or statics that outlive the scope.
- Clear all cached ExtensionLoader references in your own shutdown hooks before calling dubboBootstrap.stop().
- 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
- Do not cache ExtensionLoader references in static fields that outlive the scope model.
- Clear cached loader references in your shutdown hooks before destroying the Dubbo scope.
- Ensure cleanup callbacks and finalizers do not access extension loaders after shutdown.
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
- ExtensionDirector is destroyed
- ScopeBeanFactory is destroyed
- This instance has been destroyed
- Executor is shutdown!
- cannot be started once stopped
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/cee54c0c80eaf966.
Report an issue: GitHub.