NationalSecurityAgency/ghidra · error · RuntimeError

Timed out waiting for thread to stop

Error message

Timed out waiting for thread to stop

What it means

Raised by ghidra_util_wait_stopped when the target does not reach a stopped state within the given timeout (default 1s). The loop polls util.get_process().state every 0.1s and raises at commands.py:2092 once elapsed time exceeds the timeout. IMPORTANT BUG: the loop compares against `lldb.eStateRunnig` (typo, missing 'n'); the correct constant is `lldb.eStateRunning`. Accessing the misspelled attribute typically raises AttributeError before the timeout branch, so this exact message is effectively a dead path until the typo is fixed.

Source

Thrown at Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py:2092

    An optional timeout may be given in seconds. If omitted, the timeout is 1
    second.
    """

    args = shlex.split(command)
    if len(args) == 0:
        timeout = 1
    elif len(args) == 1:
        timeout = int(args[0])
    else:
        raise RuntimeError("Usage: ghidra util wait-stopped [SECONDS]")

    start = time.time()
    p = util.get_process()
    while p is not None and p.state == lldb.eStateRunnig:
        time.sleep(0.1)
        p = util.get_process()  # I suppose it could change
        if time.time() - start > timeout:
            raise RuntimeError('Timed out waiting for thread to stop')
    print(f"Finished wait. State={p.state}")

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Increase the timeout: `ghidra util wait-stopped 10`.
  2. Ensure the target was actually commanded to stop (breakpoint/step) before waiting; wait-stopped cannot create a stop.
  3. Fix the typo at commands.py:2088: `lldb.eStateRunning` so the poll correctly detects running state and the timeout path works as designed.
  4. If you see AttributeError mentioning eStateRunnig, that is the same line — patch the constant name.

Example fix

// before
while p is not None and p.state == lldb.eStateRunnig:
// after
while p is not None and p.state == lldb.eStateRunning:
Defensive patterns

Strategy: try-catch

Validate before calling

import lldb, time
# Guard before waiting: only wait if a stop is actually expected,
# and use the CORRECT constant (eStateRunning, not the codebase typo).
running = getattr(lldb, 'eStateRunning', None)
if running is None:
    raise AttributeError("lldb.eStateRunning missing; cannot poll state")

Try / catch

try:
    dbg.HandleCommand('ghidra util wait-stopped 10')
except RuntimeError as e:
    if 'Timed out waiting' in str(e):
        # target did not stop in time — retry with larger timeout or
        # verify a stop was actually requested
        ...

Prevention

When it happens

Trigger: Issuing `ghidra util wait-stopped` (or with N seconds) when the target is still running/resuming and doesn't stop within the window — e.g. right after `process continue` on a long-running target, or with a too-small timeout on a slow remote/gdb-protocol target. In practice you may instead hit AttributeError on `lldb.eStateRunnig`.

Common situations: Race after a continue/step where the stop event hasn't arrived; slow remote debugging where 1s default is too short; or a target that never stops (runs to exit). The typo means the intended 'running' comparison is broken.

Understand the failure class

Related errors


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