NationalSecurityAgency/ghidra · error · EarlyTerminationException

The back-end exited (code=%s) before receiving a connection

Error message

The back-end exited (code=%s) before receiving a connection

What it means

Thrown as EarlyTerminationException by acceptOrSessionEnds when the back-end CompletableFuture completes (the launched target/tracer process exited) before the Trace RMI acceptor received an incoming connection. The exit code is included. This indicates the debugger back-end died during startup, before it could phone home to Ghidra.

Source

Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/launcher/AbstractTraceRmiLaunchOffer.java:578

			SocketAddress address) throws Exception;

	protected TraceRmiHandler acceptOrSessionEnds(DefaultTraceRmiAcceptor acceptor,
			TraceRmiBackEnd backEnd)
			throws SocketException, CancelledException, EarlyTerminationException {
		acceptor.setTimeout(getConnectionTimeoutMillis());
		CompletableFuture<TraceRmiHandler> futureConnection = CompletableFuture.supplyAsync(() -> {
			try {
				return acceptor.accept();
			}
			catch (CancelledException | IOException e) {
				return ExceptionUtils.rethrow(e);
			}
		});
		// Because of accept timeout, should be a finite wait
		try {
			CompletableFuture.anyOf(backEnd, futureConnection).get();
			if (backEnd.isDone()) {
				throw new EarlyTerminationException(
					"The back-end exited (code=%s) before receiving a connection"
							.formatted(backEnd.getNow(null)));
			}
			return futureConnection.get();
		}
		catch (ExecutionException e) {
			switch (e.getCause()) {
				case CancelledException ce -> throw ce;
				default -> throw new AssertionError(e);
			}
		}
		catch (InterruptedException e) {
			throw new AssertionError(e);
		}
	}

	public static class NoStaticMappingException extends Exception {
		public NoStaticMappingException(String message) {

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Inspect the back-end terminal output for the error that caused the exit (the exit code is in the message).
  2. Verify the target binary path and all launch arguments in the launch offer configuration.
  3. Ensure required debugger/agent dependencies are installed and on PATH for the back-end.
  4. Confirm architecture compatibility between the debugger back-end and the target process.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before launching, sanity-check the target binary and back-end dependencies
Path target = Path.of(spec.getArguments().get(0));
if (!Files.isExecutable(target)) throw new IllegalStateException("Target binary missing/not executable");

Try / catch

try { handler = acceptOrSessionEnds(acceptor, backEnd); }
catch (EarlyTerminationException e) {
    // read exit code, surface back-end terminal output to user
    throw e;
}

Prevention

When it happens

Trigger: The launch offer starts a back-end process and races its termination future against the RMI accept future. If the process exits first — crash, missing library, wrong architecture, bad arguments — this fires. Common with misconfigured launch arguments, missing target binary, or the back-end script erroring out immediately.

Common situations: The target executable path is wrong or the binary is missing; the back-end requires a dependency (e.g., gdb, a JDI agent jar) not installed; the launch arguments contain a typo causing immediate exit; the back-end script printed an error to its terminal and exited with a non-zero code; architecture mismatch (e.g., 32-bit target with 64-bit debugger).

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/8320193bd2e87396. Report an issue: GitHub.