apache/dubbo · error · IllegalStateException

ApplicationModel is destroyed

Error message

ApplicationModel is destroyed

What it means

Thrown by ApplicationModel.checkDestroyed when an operation is attempted on an ApplicationModel that has already been destroyed. After destruction the model is unusable; further access (creating modules, registering configs, etc.) is rejected to prevent use-after-shutdown bugs.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/rpc/model/ApplicationModel.java:283

            this.pubModuleModels.remove(moduleModel);
            if (moduleModel == defaultModule) {
                defaultModule = findDefaultModule();
            }
        }
    }

    void tryDestroy() {
        synchronized (instLock) {
            if (this.moduleModels.isEmpty()
                    || (this.moduleModels.size() == 1 && this.moduleModels.get(0) == internalModule)) {
                destroy();
            }
        }
    }

    private void checkDestroyed() {
        if (isDestroyed()) {
            throw new IllegalStateException("ApplicationModel is destroyed");
        }
    }

    public List<ModuleModel> getModuleModels() {
        return Collections.unmodifiableList(moduleModels);
    }

    public List<ModuleModel> getPubModuleModels() {
        return Collections.unmodifiableList(pubModuleModels);
    }

    public ModuleModel getDefaultModule() {
        if (defaultModule == null) {
            synchronized (instLock) {
                if (defaultModule == null) {
                    defaultModule = findDefaultModule();
                    if (defaultModule == null) {
                        defaultModule = this.newModule();

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Ensure no tasks/threads touch the ApplicationModel after destroy(); order shutdown so Dubbo stops first or wait for in-flight work.
  2. In tests, create a fresh ApplicationModel per test rather than reusing a destroyed one.
  3. Release static/long-lived references to the model before destruction.

Example fix

// before
applicationModel.destroy();
// ... later, async task still running:
moduleModel = applicationModel.getDefaultModule(); // throws

// after
// await/quiesce async tasks BEFORE destroy, or recreate the model.
Defensive patterns

Strategy: type-guard

Validate before calling

// Guard all post-shutdown access
if (applicationModel.isDestroyed()) {
    throw new IllegalStateException(
        "ApplicationModel already destroyed; cannot proceed with this operation");
}

Type guard

static boolean isUsable(ApplicationModel m) {
    return m != null && !m.isDestroyed();
}

Try / catch

try {
    applicationModel.getDefaultModule();
} catch (IllegalStateException e) {
    if ("ApplicationModel is destroyed".equals(e.getMessage())) {
        // recreate or obtain a fresh model rather than reuse the destroyed one
        applicationModel = ApplicationModel.defaultModel();
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Any guarded operation on ApplicationModel after destroy() has run. Common in test teardown or application shutdown sequences where a background/late task touches the model after it was torn down.

Common situations: Unit/integration tests that destroy the ApplicationModel in @AfterEach but a later step still references it. Hot-reload or redeploy that destroys a model while async export/import tasks are still running. Spring context refresh/close ordering where beans outlive the Dubbo model. Static references to a model held past its lifecycle.

Related errors


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