{"record":{"id":"aefe55cef64d1fbf","repo":"junit-team/junit5","slug":"failed-to-close-custom-class-loader","errorCode":null,"errorMessage":"Failed to close custom class loader","messagePattern":"Failed to close custom class loader","errorType":"exception","errorClass":"JUnitException","httpStatus":null,"severity":"warning","filePath":"junit-platform-console/src/main/java/org/junit/platform/console/command/CustomClassLoaderCloseStrategy.java","lineNumber":38,"sourceCode":" * discovery and execution.\n */\n@API(status = INTERNAL, since = \"1.13\")\npublic enum CustomClassLoaderCloseStrategy {\n\n\t/**\n\t * Close the custom class loader after calling the\n\t * {@link org.junit.platform.launcher.Launcher} for test discovery or\n\t * execution.\n\t */\n\tCLOSE_AFTER_CALLING_LAUNCHER {\n\n\t\t@Override\n\t\tvoid handle(CustomClassLoader customClassLoader) {\n\t\t\ttry {\n\t\t\t\tcustomClassLoader.close();\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new JUnitException(\"Failed to close custom class loader\", ex);\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Rely on the JVM to release resources held by the custom class loader when\n\t * it terminates.\n\t *\n\t * <p>This mode is only safe to use when calling {@link System#exit(int)}\n\t * afterward.\n\t */\n\tKEEP_OPEN {\n\n\t\t@Override\n\t\tvoid handle(CustomClassLoader customClassLoader) {\n\t\t\t// do nothing\n\t\t}\n\t};","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-console/src/main/java/org/junit/platform/console/command/CustomClassLoaderCloseStrategy.java#L20-L56","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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).","Move the classpath root to local disk to avoid network-mounted I/O failures during close.","Verify junit-platform-console and junit-platform-launcher versions are aligned so the close strategy is the expected one."],"exampleFix":"// before: default CLOSE_AFTER_CALLING_LAUNCHER triggers close()\njava -jar junit-platform-console-standalone.jar execute --additional-classpath-entry /mnt/nfs/lib.jar\n// after: keep the loader open and rely on JVM teardown\njava -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","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Close-strategy failures happen at teardown; wrap launcher invocation if you drive it programmatically.\ntry {\n  launcher.execute(request);\n} catch (org.junit.platform.commons.JUnitException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Failed to close custom class loader\")) {\n    // tests already ran; log and continue, or switch to keep-open strategy\n    log.warn(\"Custom classloader close failed; results may still be valid.\", e);\n  } else throw e;\n}","preventionTips":["Run with the keep-open close strategy when you call System.exit afterward (e.g. the standalone jar does this).","Avoid network-mounted jars as additional classpath entries to dodge I/O failures on close.","On Windows, ensure no process holds an exclusive lock on the jar before the run."],"tags":["classloader","resource-cleanup","console-launcher","filesystem"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}