{"record":{"id":"ff381532184d7971","repo":"eclipse-vertx/vert.x","slug":"already-closed","errorCode":null,"errorMessage":"Already closed","messagePattern":"Already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"vertx-core/src/main/java/io/vertx/core/impl/TaskQueue.java","lineNumber":215,"sourceCode":"     */\n    public List<Runnable> suspendedTasks() {\n      return suspendedTasks;\n    }\n  }\n\n  /**\n   * Close the queue.\n   *\n   * @return a structure of suspended threads and pending tasks\n   */\n  public CloseResult close() {\n    List<Thread> suspendedThreads;\n    List<Runnable> suspendedTasks;\n    Thread activeThread;\n    Runnable activeTask;\n    synchronized (tasks) {\n      if (closed) {\n        throw new IllegalStateException(\"Already closed\");\n      }\n      suspendedThreads = new ArrayList<>(continuations.size());\n      suspendedTasks = new ArrayList<>(continuations.size());\n      Iterator<Task> it = tasks.iterator();\n      while (it.hasNext()) {\n        Task task = it.next();\n        if (task instanceof ContinuationTask) {\n          ContinuationTask continuationTask = (ContinuationTask) task;\n          suspendedThreads.add(continuationTask.thread);\n          suspendedTasks.add(continuationTask.task.runnable);\n          it.remove();\n        }\n      }\n      for (ContinuationTask cont : continuations) {\n        suspendedThreads.add(cont.thread);\n        suspendedTasks.add(cont.task.runnable);\n      }\n      continuations.clear();","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/eclipse-vertx/vert.x/blob/fb308bd8c3f12c79f4ae89bef67fadf6c80d036e/vertx-core/src/main/java/io/vertx/core/impl/TaskQueue.java#L197-L233","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nqueue.close();\ncleanup();\nqueue.close(); // IllegalStateException\n// after\nif (closedRef.compareAndSet(false, true)) {\n  queue.close();\n  cleanup();\n}","handlingStrategy":"try-catch","validationCode":"if (!closedRef.compareAndSet(false, true)) return; // idempotent close","typeGuard":"boolean isOpen(TaskQueue q){ return !closedRef.get(); }","tryCatchPattern":"try { queue.close(); } catch (IllegalStateException e) { if (!\"Already closed\".equals(e.getMessage())) throw e; /* ignore double close */ }","preventionTips":["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)"],"tags":["task-queue","double-close","illegal-state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"fb308bd8c3f12c79f4ae89bef67fadf6c80d036e","analyzedAt":"2026-09-06T11:37:12.241Z","contentChangedAt":"2026-09-06T11:37:12.241Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}