{"id":"ed95cd8f45ac5911","repo":"junit-team/junit5","slug":"launcher-session-has-already-been-closed","errorCode":null,"errorMessage":"Launcher session has already been closed","messagePattern":"Launcher session has already been closed","errorType":"exception","errorClass":"PreconditionViolationException","httpStatus":null,"severity":"error","filePath":"junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java","lineNumber":109,"sourceCode":"\t\t\tinterceptor.close();\n\t\t}\n\t}\n\n\t@Override\n\tpublic NamespacedHierarchicalStore<Namespace> getStore() {\n\t\treturn store;\n\t}\n\n\tprivate static class ClosedLauncher implements Launcher {\n\n\t\tstatic final ClosedLauncher INSTANCE = new ClosedLauncher();\n\n\t\tprivate ClosedLauncher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void registerTestExecutionListeners(TestExecutionListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic TestPlan discover(LauncherDiscoveryRequest launcherDiscoveryRequest) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/junit-team/junit5/blob/956246301e03d757539f7b76dd5a91f298637563/junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java#L91-L127","documentation":"Thrown by the ClosedLauncher singleton when registerLauncherDiscoveryListeners() is invoked on a Launcher whose LauncherSession has already been closed. After DefaultLauncherSession.close() swaps the delegate to ClosedLauncher.INSTANCE (line 87-88), every Launcher method routes to a stub that rejects the call. It is a PreconditionViolationException signalling use-after-close.","triggerScenarios":"Calling launcher.registerLauncherDiscoveryListeners(...) after the LauncherSession that produced the Launcher has been closed (e.g. via try-with-resources on LauncherSessionFactory.openSession()). The DelegatingLauncher held from before close now points at ClosedLauncher.INSTANCE.","commonSituations":"Storing the Launcher in a field/static, closing the session (try-with-resources block ending), then registering listeners in a later test run. IDE test runners or build tools that reuse a Launcher across sessions. Refactoring that moved session.close() earlier than listener registration.","solutions":["Register all LauncherDiscoveryListeners before closing the LauncherSession, ideally right after openSession().","Do not cache the Launcher beyond the session lifetime; obtain a fresh Launcher from a new session for each run.","If using try-with-resources, move registerLauncherDiscoveryListeners inside the try block.","Audit lifecycle code to ensure close() is the last operation on the session."],"exampleFix":"// before\ntry (var session = factory.openSession()) {\n    Launcher launcher = session.getLauncher();\n}\nlauncher.registerLauncherDiscoveryListeners(listener); // throws\n\n// after\ntry (var session = factory.openSession()) {\n    Launcher launcher = session.getLauncher();\n    launcher.registerLauncherDiscoveryListeners(listener);\n    // ... execute\n}","handlingStrategy":"validation","validationCode":"// Track session liveness before registering listeners\nif (session.isClosed()) { // you must track this; LauncherSession has no isOpen() API,\n    throw new IllegalStateException(\"session closed\"); // so mirror close() in a wrapper\n}\nlauncher.registerLauncherDiscoveryListeners(listener);","typeGuard":null,"tryCatchPattern":"try {\n    launcher.registerLauncherDiscoveryListeners(listener);\n} catch (PreconditionViolationException e) {\n    if (e.getMessage().contains(\"already been closed\")) {\n        // reopen a session and retry, or fail fast with context\n    } else { throw e; }\n}","preventionTips":["Scope all listener registration inside the LauncherSession try-with-resources block.","Never store a Launcher in a static or long-lived field that outlives its session.","Treat close() as terminal; nothing may touch the Launcher afterwards."],"tags":["lifecycle","launcher","use-after-close","junit-platform"],"analyzedSha":"956246301e03d757539f7b76dd5a91f298637563","analyzedAt":"2026-08-04T19:25:17.049Z","schemaVersion":2}