apache/dubbo · error · IllegalStateException

Exception occurred when loading extension class (interface:

Error message

Exception occurred when loading extension class (interface: {})

What it means

Thrown by getExtensionClasses() when loadExtensionClasses() raises InterruptedException while scanning and parsing META-INF/dubbo/, META-INF/services/, and META-INF/dubbo/internal/ SPI resource files. The class loading of the SPI registry is interruptible, and if the thread is interrupted (interrupt status set) during the scan, this error wraps the InterruptedException and is re-thrown as IllegalStateException. It is also logged via COMMON_ERROR_LOAD_EXTENSION.

Source

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

    }

    private Map<String, Class<?>> getExtensionClasses() {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            loadExtensionClassesLock.lock();
            try {
                classes = cachedClasses.get();
                if (classes == null) {
                    try {
                        classes = loadExtensionClasses();
                    } catch (InterruptedException e) {
                        logger.error(
                                COMMON_ERROR_LOAD_EXTENSION,
                                "",
                                "",
                                "Exception occurred when loading extension class (interface: " + type + ")",
                                e);
                        throw new IllegalStateException(
                                "Exception occurred when loading extension class (interface: " + type + ")", e);
                    }
                    cachedClasses.set(classes);
                }
            } finally {
                loadExtensionClassesLock.unlock();
            }
        }
        return classes;
    }

    /**
     * synchronized in getExtensionClasses
     */
    @SuppressWarnings("deprecation")
    private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
        checkDestroyed();
        cacheDefaultExtensionName();

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Ensure SPI extensions are loaded eagerly at startup (call getSupportedExtensions() during boot) before the application reaches a state where threads may be interrupted.
  2. If the thread was legitimately interrupted, restore the interrupt status in your catch block (Thread.currentThread().interrupt()) and let the caller decide whether to retry.
  3. Avoid calling getExtension/getAdaptiveExtension from threads that may be interrupted mid-flight (shutdown hooks, cancelled tasks).

Example fix

// before: lazy load races with shutdown interrupt
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    loader.getExtension("x"); // may throw [144] if interrupt overlaps
}));

// after: pre-load at startup, restore interrupt in handler
loader.getSupportedExtensions(); // warm the cache early
try {
    loader.getExtension("x");
} catch (IllegalStateException e) {
    if (Thread.interrupted()) { /* shutdown race; expected */ }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Warm the SPI cache early in the lifecycle before interrupts are possible
extensionDirector.getExtensionLoader(MySpi.class).getSupportedExtensions();

Try / catch

try {
    loader.getExtension(name);
} catch (IllegalStateException e) {
    if (e.getCause() instanceof InterruptedException) {
        Thread.currentThread().interrupt(); // restore status
        log.warn("SPI load interrupted; will retry on next access");
    } else throw e;
}

Prevention

When it happens

Trigger: The thread performing the first lazy load of extension classes (inside the loadExtensionClassesLock) receives Thread.interrupt() during the resource enumeration or URL stream reading of SPI config files. This typically happens in app shutdown hooks, thread-pool reclamation, or frameworks that interrupt worker threads.

Common situations: Application shutdown races with lazy SPI initialization; a thread pool that interrupts idle threads while Dubbo is still warming up; running inside a container/scheduler that sets interrupt status; very slow classpath/IO making the SPI scan overlap with an interrupt-driven lifecycle event.

Related errors


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