NationalSecurityAgency/ghidra · error · RuntimeError

Transaction already started

Error message

Transaction already started

What it means

Raised as RuntimeError by State.require_no_tx() when STATE.tx is already set — a transaction is open and nesting is not allowed. Attempting to open a second transaction before the first closes triggers it.

Source

Thrown at Ghidra/Debug/Debugger-agent-drgn/src/main/py/src/ghidradrgn/commands.py:138

    def require_no_trace(self) -> None:
        if self.trace != None:
            raise RuntimeError("Trace already started")

    def reset_trace(self) -> None:
        self.trace: Optional[Trace[Extra]] = None
        util.set_convenience_variable('_ghidra_tracing', "false")
        self.reset_tx()

    def require_tx(self) -> Tuple[Trace, Transaction]:
        trace = self.require_trace()
        if self.tx is None:
            raise RuntimeError("No transaction")
        return trace, self.tx

    def require_no_tx(self) -> None:
        if self.tx != None:
            raise RuntimeError("Transaction already started")

    def reset_tx(self) -> None:
        self.tx: Optional[Transaction] = None


STATE = State()


def ghidra_trace_connect(address: Optional[str] = None) -> None:
    """
    Connect Python to Ghidra for tracing

    Address must be of the form 'host:port'
    """

    STATE.require_no_client()
    if address is None:
        raise RuntimeError(

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Re-use the existing open transaction instead of opening a nested one.
  2. Guard: if STATE.tx is not None: perform the work in the current tx rather than opening another.
  3. Ensure earlier open_tx blocks are exited (context manager) before starting a new one.

Example fix

// before
with trace.open_tx('outer'):
    with trace.open_tx('inner'):  # RuntimeError: Transaction already started
        ...
// after
with trace.open_tx('outer'):
    ...  # do inner work in the same transaction
Defensive patterns

Strategy: validation

Validate before calling

if STATE.tx is not None:
    raise RuntimeError('a transaction is already open; reuse it instead of nesting')

Type guard

def no_open_transaction() -> bool:
    return getattr(STATE, 'tx', None) is None

Try / catch

try:
    with trace.open_tx('op'):
        do_work(trace)
except RuntimeError as e:
    if 'Transaction already started' in str(e):
        do_work(trace)  # reuse the ambient transaction
    else:
        raise

Prevention

When it happens

Trigger: Calling open_tx() (or a helper that opens one) while already inside an open_tx block.

Common situations: Nested open_tx() calls; a helper that opens its own tx invoked from within another tx; a tx left open by an exception.

Related errors


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