eclipse-vertx/vert.x · warning · IllegalStateException
Already closed
Error message
Already closed
What it means
TaskQueue.close() drains and suspends outstanding tasks and threads; calling it a second time finds the closed flag set and throws IllegalStateException('Already closed'). The queue cannot be closed twice nor reused after close.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/impl/TaskQueue.java:215
*/
public List<Runnable> suspendedTasks() {
return suspendedTasks;
}
}
/**
* Close the queue.
*
* @return a structure of suspended threads and pending tasks
*/
public CloseResult close() {
List<Thread> suspendedThreads;
List<Runnable> suspendedTasks;
Thread activeThread;
Runnable activeTask;
synchronized (tasks) {
if (closed) {
throw new IllegalStateException("Already closed");
}
suspendedThreads = new ArrayList<>(continuations.size());
suspendedTasks = new ArrayList<>(continuations.size());
Iterator<Task> it = tasks.iterator();
while (it.hasNext()) {
Task task = it.next();
if (task instanceof ContinuationTask) {
ContinuationTask continuationTask = (ContinuationTask) task;
suspendedThreads.add(continuationTask.thread);
suspendedTasks.add(continuationTask.task.runnable);
it.remove();
}
}
for (ContinuationTask cont : continuations) {
suspendedThreads.add(cont.thread);
suspendedTasks.add(cont.task.runnable);
}
continuations.clear();View on GitHub (pinned to fb308bd8c3)
Solutions
- Track closure with an AtomicBoolean and skip the second close()
- Route all shutdown through one owner (single lifecycle hook) instead of multiple call sites
- If sharing the queue, wrap it in a facade exposing an idempotent close
- Do not reuse a closed TaskQueue — create a new one
Example fix
// before
queue.close();
cleanup();
queue.close(); // IllegalStateException
// after
if (closedRef.compareAndSet(false, true)) {
queue.close();
cleanup();
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!closedRef.compareAndSet(false, true)) return; // idempotent close
Type guard
boolean isOpen(TaskQueue q){ return !closedRef.get(); } Try / catch
try { queue.close(); } catch (IllegalStateException e) { if (!"Already closed".equals(e.getMessage())) throw e; /* ignore double close */ } Prevention
- Centralize shutdown in one lifecycle hook
- Guard close() with an AtomicBoolean for idempotency
- Never submit to a TaskQueue after initiating close
- Avoid closing queues you don't own (e.g. Vert.x internal queues)
When it happens
Trigger: Calling close() twice on the same TaskQueue; submitting after close (related) ; concurrent close from multiple threads racing; lifecycle code that closes on both failure and completion paths (tests result/th/testCloseBeforeResumeExecution/res/testSubmitAfterClose exercise these paths).
Common situations: Double shutdown in finally blocks without idempotency guard; closing a shared TaskQueue owned by a Vert.x internal (e.g. context-ordered execution) from user code; shutting down a worker pool twice.
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
- Already started
- Event Bus is not started
- Server already bound
- Resource manager shutdown
- Resource manager closed
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/ff381532184d7971.
Report an issue: GitHub.