apache/hadoop · warning · IllegalStateException

Shutdown in progress, cannot remove a shutdownHook

Error message

Shutdown in progress, cannot remove a shutdownHook

What it means

ShutdownHookManager.removeShutdownHook throws IllegalStateException when called while the JVM shutdown sequence is running: the hook registry is being iterated and executed, so mutation is disallowed. Removal during shutdown is a lifecycle race in cleanup code, not a registry corruption.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ShutdownHookManager.java:344

    if (shutdownInProgress.get()) {
      throw new IllegalStateException("Shutdown in progress, cannot add a " +
          "shutdownHook");
    }
    hooks.add(new HookEntry(shutdownHook, priority, timeout, unit));
  }

  /**
   * Removes a shutdownHook.
   *
   * @param shutdownHook shutdownHook to remove.
   * @return TRUE if the shutdownHook was registered and removed,
   * FALSE otherwise.
   */
  @InterfaceAudience.Public
  @InterfaceStability.Stable
  public boolean removeShutdownHook(Runnable shutdownHook) {
    if (shutdownInProgress.get()) {
      throw new IllegalStateException("Shutdown in progress, cannot remove a " +
          "shutdownHook");
    }
    // hooks are only == by runnable
    return hooks.remove(new HookEntry(shutdownHook, 0, TIMEOUT_MINIMUM,
      TIME_UNIT_DEFAULT));
  }

  /**
   * 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.
   */
  @InterfaceAudience.Public
  @InterfaceStability.Stable
  public boolean hasShutdownHook(Runnable shutdownHook) {
    return hooks.contains(new HookEntry(shutdownHook, 0, TIMEOUT_MINIMUM,
      TIME_UNIT_DEFAULT));

View on GitHub (pinned to 2add963021)

Solutions

  1. Wrap removal in try/catch(IllegalStateException) and treat it as a no-op — the JVM is exiting anyway
  2. Guard with an AtomicBoolean closed flag so deregistration runs at most once, before shutdown starts
  3. Never call removeShutdownHook from inside another shutdown hook
  4. Pre-check with ShutdownHookManager.get().isShutdownInProgress() and skip removal when true

Example fix

// before
public void close() {
  ShutdownHookManager.get().removeShutdownHook(hook); // throws if JVM exiting
}

// after
private final AtomicBoolean closed = new AtomicBoolean(false);
public void close() {
  if (!closed.compareAndSet(false, true)) return;
  try {
    ShutdownHookManager.get().removeShutdownHook(hook);
  } catch (IllegalStateException e) {
    // JVM shutdown in progress: nothing to deregister
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

ShutdownHookManager mgr = ShutdownHookManager.get();
if (!mgr.isShutdownInProgress()) {
  mgr.removeShutdownHook(hook);
}

Try / catch

try {
  ShutdownHookManager.get().removeShutdownHook(hook);
} catch (IllegalStateException e) {
  // JVM exiting: registry is being drained, removal is moot
}

Prevention

When it happens

Trigger: A service close()/destroy() that deregisters its hook being invoked from another shutdown hook or from a thread that notices SIGTERM after shutdown began — double teardown paths (container stop plus JVM hook).

Common situations: Idempotent close() called once by webapp undeploy and once by the shutdown hook; test frameworks deregistering hooks in @AfterClass while the JVM is exiting; watchdogs that trigger cleanup concurrently with exit.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/1dc5ec753526f6e7. Report an issue: GitHub.