apache/shenyu · error · IllegalStateException

Shutdown in progress, cannot remove a shutdownHook

Error message

Shutdown in progress, cannot remove a shutdownHook

What it means

removeShutdownHook unregisters a previously added hook. Once shutdown has started, mutation of the hook list is forbidden (hooks are being executed in priority order), so any removal attempt throws IllegalStateException regardless of whether the hook was registered.

Solutions

  1. Remove hooks during normal application lifetime only; accept that during shutdown removal is a no-op and wrap the call in a state check.
  2. Check isShutdownInProgress() before removing and skip silently.
  3. Redesign so hook execution itself is idempotent, making removal unnecessary.

Example fix

// before
manager.removeShutdownHook(hook);
// after
if (!manager.isShutdownInProgress()) {
    manager.removeShutdownHook(hook);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!manager.isShutdownInProgress()) {
    manager.removeShutdownHook(hook);
}

Try / catch

try {
    manager.removeShutdownHook(hook);
} catch (IllegalStateException e) {
    // shutdown started; removal unnecessary, hook teardown is idempotent
}

Prevention

When it happens

Trigger: Calling removeShutdownHook during JVM shutdown — e.g. a Spring destroy callback or a hook that tries to cancel peer hooks while the manager is executing them.

Common situations: Cancel-on-shutdown patterns where a component detaches its hook after it has already cleaned up; double-shutdown in tests (closing two contexts).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/fb7ad681ee5a1170. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShutdownHookManager.java:135

     */
    public void addShutdownHook(final Runnable shutdownHook, final int priority) {
        if (Objects.isNull(shutdownHook)) {
            throw new IllegalArgumentException("shutdownHook cannot be NULL");
        }
        if (shutdownInProgress.get()) {
            throw new IllegalStateException("Shutdown in progress, cannot add a shutdownHook");
        }
        hooks.add(new HookEntry(shutdownHook, priority));
    }

    /**
     * Removes a shutdownHook.
     * @param shutdownHook shutdownHook to remove.
     * @return TRUE if the shutdownHook was registered and removed,FALSE otherwise.
     */
    public boolean removeShutdownHook(final Runnable shutdownHook) {
        if (shutdownInProgress.get()) {
            throw new IllegalStateException("Shutdown in progress, cannot remove a shutdownHook");
        }
        return hooks.remove(new HookEntry(shutdownHook, 0));
    }

    /**
     * Indicates if a shutdownHook is registered or not.
     *
     * @param shutdownHook shutdownHook to check if registered.
     * @return TRUE/FALSE depending if the shutdownHook is is registered.
     */
    public boolean hasShutdownHook(final Runnable shutdownHook) {
        return hooks.contains(new HookEntry(shutdownHook, 0));
    }

    /**
     * Indicates if shutdown is in progress or not.
     *
     * @return TRUE if the shutdown is in progress, otherwise FALSE.

View on GitHub (pinned to 567142e072)