NationalSecurityAgency/ghidra · error · RuntimeError

Transaction already started

Error message

Transaction already started

What it means

Raised by State.require_no_tx in the Ghidra LLDB agent when STATE.tx is already set — i.e. a transaction is already open. The start-tx command (commands.py:525 calls require_no_tx) calls this to prevent nesting two transactions; an existing open transaction is rejected with RuntimeError.

Source

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

    def require_no_trace(self) -> None:
        if self.trace is not 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[Extra], 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 is not None:
            raise RuntimeError("Transaction already started")

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


STATE = State()

if __name__ == '__main__':
    lldb.SBDebugger.InitializeWithErrorHandling()
    lldb.debugger = lldb.SBDebugger.Create()
elif lldb.debugger:
    lldb.debugger.HandleCommand(
        'command container add -h "Commands for connecting to Ghidra" ghidra')
    lldb.debugger.HandleCommand(
        'command container add -h "Commands for exporting data to a Ghidra trace" ghidra trace')
    lldb.debugger.HandleCommand(
        'command container add -h "Utility commands for testing with Ghidra" ghidra util')

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Commit/close the current transaction (ghidra_trace_tx_commit, commands.py:542) before starting a new one.
  2. Use STATE.require_trace().open_tx(description) as a context manager so the tx always closes on block exit.
  3. Catch RuntimeError and hint 'transaction already started; run ghidra_trace_tx_commit first'.
  4. Ensure reset_tx is invoked after commit so STATE.tx returns to None.

Example fix

// before
STATE.require_no_tx()
STATE.tx = STATE.require_trace().start_tx(description, undoable=False)
// after
if STATE.tx is not None:
    STATE.require_tx()[1].commit()
    STATE.reset_tx()
STATE.require_no_tx()
STATE.tx = STATE.require_trace().start_tx(description, undoable=False)
Defensive patterns

Strategy: validation

Validate before calling

if STATE.tx is not None:
    STATE.require_tx()[1].commit()
    STATE.reset_tx()
STATE.require_no_tx()
STATE.tx = STATE.require_trace().start_tx(description, undoable=False)

Type guard

def no_tx_open() -> bool:
    return STATE.tx is None

Try / catch

try:
    STATE.require_no_tx()
except RuntimeError:
    print("Transaction already started; run ghidra_trace_tx_commit first")
    return

Prevention

When it happens

Trigger: Running the start-tx command a second time without first committing/closing the existing transaction. The start-tx command asserts require_no_tx before opening a new Transaction.

Common situations: User runs 'ghidra_trace_tx_start' twice; previous tx was not committed (ghidra_trace_tx_commit); script opens a second tx without closing the first.

Related errors


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