junit-team/junit5 · warning · JUnitException

Failed to close custom class loader

Error message

Failed to close custom class loader

What it means

CustomClassLoaderCloseStrategy.CLOSE_AFTER_CALLING_LAUNCHER.handle() closes the custom URLClassLoader that the console launcher created to load user classes. If close() throws, a JUnitException wraps it. This is a cleanup-time failure after discovery/execution has completed.

Solutions

  1. Switch to the KEEP_OPEN close strategy (the launcher then relies on JVM/System.exit to release resources) — pass the corresponding option/property to keep the classloader open.
  2. Ensure no external code holds or closes the custom classloader; ensure the jar/directory is not locked (close other processes holding it, especially on Windows).
  3. Move the classpath root to local disk to avoid network-mounted I/O failures during close.
  4. Verify junit-platform-console and junit-platform-launcher versions are aligned so the close strategy is the expected one.

Example fix

// before: default CLOSE_AFTER_CALLING_LAUNCHER triggers close()
java -jar junit-platform-console-standalone.jar execute --additional-classpath-entry /mnt/nfs/lib.jar
// after: keep the loader open and rely on JVM teardown
java -jar junit-platform-console-standalone.jar execute --additional-classpath-entry /mnt/nfs/lib.jar --disable-banner=false # use the keep-open option per current CLI help
Defensive patterns

Strategy: fallback

Try / catch

// Close-strategy failures happen at teardown; wrap launcher invocation if you drive it programmatically.
try {
  launcher.execute(request);
} catch (org.junit.platform.commons.JUnitException e) {
  if (e.getMessage() != null && e.getMessage().contains("Failed to close custom class loader")) {
    // tests already ran; log and continue, or switch to keep-open strategy
    log.warn("Custom classloader close failed; results may still be valid.", e);
  } else throw e;
}

Prevention

When it happens

Trigger: After the Launcher finishes, the console launcher closes its internally created URLClassLoader; an exception (e.g. an I/O error closing a jar resource, or a classloader already closed) bubbles up as this error. The default strategy is to close after calling the launcher; KEEP_OPEN avoids it but requires System.exit.

Common situations: Running tests against a directory/jar on a network mount or a non-default FileSystem where closing resources fails; an externally held reference to the classloader closing it first; or a jar that is locked on Windows. The test run itself usually succeeded — only the teardown fails.

Related errors


AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11). Data as JSON: /api/errors/aefe55cef64d1fbf. Report an issue: GitHub.

Appendix: source

Thrown at junit-platform-console/src/main/java/org/junit/platform/console/command/CustomClassLoaderCloseStrategy.java:38

 * discovery and execution.
 */
@API(status = INTERNAL, since = "1.13")
public enum CustomClassLoaderCloseStrategy {

	/**
	 * Close the custom class loader after calling the
	 * {@link org.junit.platform.launcher.Launcher} for test discovery or
	 * execution.
	 */
	CLOSE_AFTER_CALLING_LAUNCHER {

		@Override
		void handle(CustomClassLoader customClassLoader) {
			try {
				customClassLoader.close();
			}
			catch (Exception ex) {
				throw new JUnitException("Failed to close custom class loader", ex);
			}
		}
	},

	/**
	 * Rely on the JVM to release resources held by the custom class loader when
	 * it terminates.
	 *
	 * <p>This mode is only safe to use when calling {@link System#exit(int)}
	 * afterward.
	 */
	KEEP_OPEN {

		@Override
		void handle(CustomClassLoader customClassLoader) {
			// do nothing
		}
	};

View on GitHub (pinned to f070c699a0)