NationalSecurityAgency/ghidra · error · RuntimeException

Timed out waiting for thread to stop

Error message

Timed out waiting for thread to stop

What it means

Thrown by ghidraTraceWaitStopped(int timeout) when the target thread does not report isSuspended()==true within the given timeout milliseconds. The method polls every 100ms, and if elapsed time exceeds the timeout it aborts — the trace cannot proceed because operations require a stopped target.

Source

Thrown at Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java:2177

	}

	public void ghidraTraceSyncSynthStopped() {
		connector.getHooks().onStop(null, state.trace);
	}

	public void ghidraTraceWaitStopped(int timeout) {
		ThreadReference currentThread = connector.getJdi().getCurrentThread();
		if (currentThread == null) {
			return;
		}
		long start = System.currentTimeMillis();
		while (!currentThread.isSuspended()) {
			currentThread = connector.getJdi().getCurrentThread();
			try {
				Thread.sleep(100);
				long elapsed = System.currentTimeMillis() - start;
				if (elapsed > timeout) {
					throw new RuntimeException("Timed out waiting for thread to stop");
				}
			}
			catch (InterruptedException e) {
				Msg.error(this, "Wait interrupted");
			}
		}
	}

	public void execute(ClassType ct, ThreadReference thread, Method method, List<Value> args,
			int options) {
		try {
			Value val = ct.invokeMethod(thread, method, args, options);
			System.err.println(val);
		}
		catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Increase the timeout value passed to ghidraTraceWaitStopped to accommodate a slow target.
  2. Confirm the suspend request or breakpoint actually reached the target VM (check JDI event requests).
  3. If the thread is genuinely stuck, interrupt or forcibly suspend it via the connector before waiting.
  4. Re-issue the wait after a short delay in case of transient event-delivery latency.

Example fix

// before
ghidraTraceWaitStopped(1000); // too short, throws

// after
ghidraTraceWaitStopped(10000); // allow more time for the suspend to land
Defensive patterns

Strategy: retry

Validate before calling

// Verify suspend before waiting
ThreadReference t = connector.getJdi().getCurrentThread();
if (t != null && t.isSuspended()) { /* already stopped, no wait needed */ }
else { /* choose a generous timeout */ }

Try / catch

try { ghidraTraceWaitStopped(timeoutMs); }
catch (RuntimeException e) {
    if (e.getMessage().contains("Timed out")) { /* optionally re-issue once with a larger timeout */ }
    else throw e;
}

Prevention

When it happens

Trigger: Calling ghidraTraceWaitStopped after requesting a suspend (or expecting a breakpoint hit) but the thread keeps running. Causes: the suspend request is still in flight, the target is executing a long native call, the breakpoint was never hit, or the JDI event stream is delayed.

Common situations: Timeout value too small for a slow or heavily loaded target; the breakpoint condition was never satisfied; the target is stuck in a native/JIT-compiled region that doesn't honor suspend promptly; event delivery latency over a remote JDI connection.

Understand the failure class

Related errors


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