quarkusio/quarkus · error · IllegalStateException
Unable to destroy contextual instance of " + bean
Error message
Unable to destroy contextual instance of " + bean
What it means
InjectableContext.destroy(ContextState) iterates every contextual instance held in the state and calls destroy(bean) for each; if any bean's destruction throws an Exception, it is wrapped in an IllegalStateException('Unable to destroy contextual instance of ' + bean) with the original as cause. This signals that a @PreDestroy/@Disposes callback or custom destroy() failed while the context was shutting down.
Source
Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/InjectableContext.java:76
return result;
}
return get(contextual, creationalContextFunction.apply(contextual));
}
/**
* Destroy all contextual instances from the given state.
* <p>
* The default implementation is not optimized and does not guarantee proper sychronization. Implementations of this
* interface are encouraged to provide an optimized implementation of this method.
*
* @param state
*/
default void destroy(ContextState state) {
for (InjectableBean<?> bean : state.getContextualInstances().keySet()) {
try {
destroy(bean);
} catch (Exception e) {
throw new IllegalStateException("Unable to destroy contextual instance of " + bean, e);
}
}
}
/**
*
* @return {@code true} if this context represents a normal scope
*/
default boolean isNormal() {
return getScope().isAnnotationPresent(NormalScope.class);
}
interface ContextState {
/**
* @return an immutable map of contextual instances
*/
Map<InjectableBean<?>, Object> getContextualInstances();View on GitHub (pinned to e1c734241f)
Solutions
- Look at the cause chain to find which bean's destroy failed and fix that bean's @PreDestroy/@Disposes/destroy() logic to be idempotent and exception-safe.
- Guard resource cleanup in destroy callbacks with null/ordering checks and catch-and-log non-fatal cleanup errors.
- Ensure destroy callbacks don't depend on services already stopped during shutdown ordering.
- If the failing bean comes from an extension, report/upgrade that extension — its destroy logic is faulty.
Example fix
// before
@PreDestroy
void close() {
client.close(); // throws if already closed
}
// after
@PreDestroy
void close() {
if (client != null) {
try {
client.close();
} catch (RuntimeException e) {
log.warn("Client close failed", e);
}
}
} Defensive patterns
Strategy: try-catch
Validate before calling
for (InjectableBean<?> bean : state.getContextualInstances().keySet()) {
// pre-check: beans whose destroy may fail should log-and-continue via their own @PreDestroy guards
Objects.requireNonNull(bean, "contextual instance must not be null");
} Try / catch
try {
context.destroy(state);
} catch (IllegalStateException e) {
log.warn("Context cleanup failed", e.getCause()); // inspect the per-bean cause
} Prevention
- Make every @PreDestroy/@Disposes/destroy() idempotent and non-throwing (catch and log cleanup failures).
- Avoid depending on already-stopped services inside destroy callbacks.
- Test application shutdown paths, especially with containers/databases stopping first.
When it happens
Trigger: Destroying a custom context or a ContextState (e.g. at application shutdown, session end, or request termination) when one of the contained beans' destroy methods — or a @PreDestroy callback / @Disposes method — throws an exception.
Common situations: @PreDestroy code closing resources that are already closed or null due to ordering; destroy callbacks throwing on shutdown because a backing server/database is gone; buggy custom InjectableBean.destroy() implementations; resource cleanup failing during hot reload.
Related errors
- Multiple @PostConstruct interceptor methods declared on clas
- Multiple @PreDestroy interceptor methods declared on class:
- The application is stopping
- The application has not been started
- Quarkus does not support CDI Full @Specializes annotation; t
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/883e2fd7e7b76963.
Report an issue: GitHub.