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
- Wrap removal in try/catch(IllegalStateException) and treat it as a no-op — the JVM is exiting anyway
- Guard with an AtomicBoolean closed flag so deregistration runs at most once, before shutdown starts
- Never call removeShutdownHook from inside another shutdown hook
- 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
- Make close() idempotent with an AtomicBoolean so deregistration runs at most once, before shutdown
- Never remove hooks from inside another shutdown hook
- Pre-check isShutdownInProgress() before removal in teardown paths that can run during exit
- Treat this exception as expected noise during JVM shutdown; log at debug
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
- Shutdown in progress, cannot add a shutdownHook
- shutdownHook cannot be NULL
- AsyncDataService is already shutdown
- key + ": Stream is closed!"
- Stream closed
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/1dc5ec753526f6e7.
Report an issue: GitHub.