junit-team/junit5 · error · PreconditionViolationException
Launcher session has already been closed
Error message
Launcher session has already been closed
What it means
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.
Source
Thrown at junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java:109
interceptor.close();
}
}
@Override
public NamespacedHierarchicalStore<Namespace> getStore() {
return store;
}
private static class ClosedLauncher implements Launcher {
static final ClosedLauncher INSTANCE = new ClosedLauncher();
private ClosedLauncher() {
}
@Override
public void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {
throw new PreconditionViolationException("Launcher session has already been closed");
}
@Override
public void registerTestExecutionListeners(TestExecutionListener... listeners) {
throw new PreconditionViolationException("Launcher session has already been closed");
}
@Override
public TestPlan discover(LauncherDiscoveryRequest launcherDiscoveryRequest) {
throw new PreconditionViolationException("Launcher session has already been closed");
}
@Override
public void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners) {
throw new PreconditionViolationException("Launcher session has already been closed");
}
@OverrideView on GitHub (pinned to 956246301e)
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.
Example fix
// before
try (var session = factory.openSession()) {
Launcher launcher = session.getLauncher();
}
launcher.registerLauncherDiscoveryListeners(listener); // throws
// after
try (var session = factory.openSession()) {
Launcher launcher = session.getLauncher();
launcher.registerLauncherDiscoveryListeners(listener);
// ... execute
} Defensive patterns
Strategy: validation
Validate before calling
// Track session liveness before registering listeners
if (session.isClosed()) { // you must track this; LauncherSession has no isOpen() API,
throw new IllegalStateException("session closed"); // so mirror close() in a wrapper
}
launcher.registerLauncherDiscoveryListeners(listener); Try / catch
try {
launcher.registerLauncherDiscoveryListeners(listener);
} catch (PreconditionViolationException e) {
if (e.getMessage().contains("already been closed")) {
// reopen a session and retry, or fail fast with context
} else { throw e; }
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A NamespacedHierarchicalStore cannot be modified or queried
- OutputDirectoryCreator not available; probably due to unalig
- TestPlan must only be executed once
- Invalid LauncherPhase '%s' set via the '%s' configuration pa
- Failed to close XML events file
AI-assisted analysis of junit-team/junit5@956246301e (2026-08-04).
Data as JSON: /data/errors/ed95cd8f45ac5911.json.
Report an issue: GitHub.