junit-team/junit5 · error · UncheckedIOException

Failed to start process

Error message

Failed to start process

What it means

Thrown as an UncheckedIOException by GitInfoCollector.ProcessExecutor.startProcess() when ProcessBuilder.start() fails to spawn the git child process. Note: startProcess is called OUTSIDE exec()'s try block, so this exception propagates even during the initial 'git --version' availability probe — it is not swallowed by exec()'s 'catch (IOException ignore)'. It ultimately surfaces wrapped inside error 145 ('Failed to initialize XML events writer'). GitInfoCollector is a package-private interface invoked only when junit.platform.reporting.open.xml.git.enabled is true.

Solutions

  1. Disable git metadata collection: -Djunit.platform.reporting.open.xml.git.enabled=false (the default).
  2. Install git in the CI/container image and ensure it is on the PATH of the JVM process.
  3. Raise process/file-descriptor limits (ulimit -u, ulimit -n) on the build agent.
  4. Confirm the listener's working directory still exists at testPlanExecutionStarted time.

Example fix

// before
-Djunit.platform.reporting.open.xml.git.enabled=true

// after
-Djunit.platform.reporting.open.xml.git.enabled=false
Defensive patterns

Strategy: fallback

Validate before calling

// Only enable git collection if git is actually runnable on this host.
boolean gitEnabled = Boolean.getBoolean(
    "junit.platform.reporting.open.xml.git.enabled");
if (gitEnabled) {
    try {
        Process p = new ProcessBuilder("git", "--version").start();
        boolean ok = p.waitFor(5, TimeUnit.SECONDS) && p.exitValue() == 0;
        p.destroyForcibly();
        if (!ok) {
            System.clearProperty("junit.platform.reporting.open.xml.git.enabled");
        }
    } catch (IOException | InterruptedException e) {
        System.clearProperty("junit.platform.reporting.open.xml.git.enabled");
    }
}

Try / catch

// Users do not call GitInfoCollector directly (package-private). It surfaces wrapped
// as JUnitException("Failed to initialize XML events writer"). Catch at the launcher level:
try {
    launcher.execute(request);
} catch (JUnitException e) {
    Throwable c = e.getCause();
    if (c instanceof UncheckedIOException uio
            && uio.getMessage().equals("Failed to start process")) {
        // Git unavailable in this environment; disable and retry, or report and skip.
    }
}

Prevention

When it happens

Trigger: Enabling junit.platform.reporting.open.xml.git.enabled=true on a machine where the git binary is not installed or not on the PATH; the OS denying fork()/exec() due to pid or file-descriptor limits (cgroup-constrained containers); workingDir.toFile() returning null because the configured working dir is not a directory; the git binary disappearing between the version probe and a later command.

Common situations: Minimal Docker/Alpine CI images that omit git; distroless JVM images; sandboxes (sshd chroot, macOS sandbox) that block process spawning; ulimit -u/-n hit on a busy build agent; the working directory was deleted between the listener constructor (Path.of('.').toAbsolutePath()) and git collection.

Related errors


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

Appendix: source

Thrown at junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/GitInfoCollector.java:171

			catch (IOException ignore) {
				return Optional.empty();
			}
			finally {
				process.destroyForcibly();
			}
		}

		private static BufferedReader newBufferedReader(InputStream stream) {
			return new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset()));
		}

		private Process startProcess(String[] command) {
			Process process;
			try {
				process = new ProcessBuilder().directory(workingDir.toFile()).command(command).start();
			}
			catch (IOException e) {
				throw new UncheckedIOException("Failed to start process", e);
			}
			return process;
		}

		private static void readAllChars(Reader reader, BiConsumer<char[], Integer> consumer) throws IOException {
			char[] buffer = new char[1024];
			int numChars;
			while ((numChars = reader.read(buffer)) != -1) {
				consumer.accept(buffer, numChars);
			}
		}

		private static String trimAtEnd(StringBuilder value) {
			int endIndex = value.length();
			for (int i = value.length() - 1; i >= 0; i--) {
				if (Character.isWhitespace(value.charAt(i))) {
					endIndex--;
					break;

View on GitHub (pinned to f070c699a0)