quarkusio/quarkus · error · IllegalStateException
Instance already destroyed
Error message
Instance already destroyed
What it means
AbstractInstanceHandle.get() throws IllegalStateException if the handle has already been destroyed. InstanceHandle/InstanceHandleImpl and similar wrappers track a destroyed flag; once destroy() runs, the instance is no longer retrievable. This guards use-after-free of contextual instances.
Source
Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/AbstractInstanceHandle.java:40
private final CreationalContext<T> creationalContext;
private final CreationalContext<?> parentCreationalContext;
private final Consumer<T> destroyLogic;
// values: 0="not destroyed", 1="destroyed"
private volatile int destroyed;
AbstractInstanceHandle(InjectableBean<T> bean, CreationalContext<T> creationalContext,
CreationalContext<?> parentCreationalContext, Consumer<T> destroyLogic) {
this.bean = bean;
this.creationalContext = creationalContext;
this.parentCreationalContext = parentCreationalContext;
this.destroyLogic = destroyLogic;
}
@Override
public T get() {
if (destroyed != 0) {
throw new IllegalStateException("Instance already destroyed");
}
return instanceInternal();
}
@Override
public InjectableBean<T> getBean() {
return bean;
}
protected abstract boolean isInstanceCreated();
protected abstract T instanceInternal();
@Override
public void destroy() {
if (isInstanceCreated() && DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
if (destroyLogic != null) {
destroyLogic.accept(instanceInternal());View on GitHub (pinned to e1c734241f)
Solutions
- Call get() before destroy(), and keep the returned reference for later use
- Do not destroy a handle while it may still be read; re-obtain a fresh handle via Arc.container().instance(...) if needed
- Track destroyed state yourself and skip get() when already destroyed
- Check for accidental double-destroy logic (destroy called twice, then get)
Example fix
// before handle.destroy(); Foo foo = handle.get(); // IllegalStateException // after Foo foo = handle.get(); // obtain first handle.destroy(); // then destroy
Defensive patterns
Strategy: type-guard
Type guard
boolean usable = (handle instanceof InstanceHandle<?> h) && !h.isDestroyed();
Try / catch
try { Foo f = handle.get(); } catch (IllegalStateException e) { Foo f = Arc.container().instance(Foo.class).get(); } Prevention
- Consume get() before calling destroy()
- Never cache InstanceHandle longer than its lifecycle owner
- Centralize destroy logic to avoid double-destroy/use-after-destroy
When it happens
Trigger: Calling handle.get() after handle.destroy() on the same InstanceHandle, e.g. storing the handle in a field, consuming it, then re-reading it later in request shutdown or cleanup code.
Common situations: Caching an InstanceHandle across requests and destroying it at the end of one request; double-cleanup in @PreDestroy or shutdown hooks calling destroy then get; frameworks that auto-destroy handles on context close.
Related errors
- The Reactive REST Client needs to be built within the contex
- Multiple @PostConstruct interceptor methods declared on clas
- Multiple @PreDestroy interceptor methods declared on class:
- Unable to destroy contextual instance of " + bean
- Cannot destroy instance of " + bean + " - no active context
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/2c154c7c7568b9b2.
Report an issue: GitHub.