NationalSecurityAgency/ghidra · error · RuntimeError
No transaction
Error message
No transaction
What it means
Raised by State.require_tx() (commands.py:148-149) when a trace is active but no Trace transaction (STATE.tx) is open. Ghidra traces are mutated inside Transaction objects; mutating the trace tree, memory, or registers outside a transaction is illegal. require_tx() is the precondition gate for nearly every write command.
Source
Thrown at Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/commands.py:149
def require_trace(self) -> Trace[Extra]:
if self.trace is None:
raise RuntimeError("No trace active")
return self.trace
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'
"""View on GitHub (pinned to d5f144c24d)
Solutions
- Wrap every mutating command pair in ghidra_trace_tx_start('description') ... ghidra_trace_tx_commit().
- If unsure whether a tx is open, guard it: call require_no_tx-free logic by committing any open tx first, then opening a fresh one.
- Reset session state with ghidra_trace_disconnect/stop and replay the full connect->start->tx_start sequence.
Example fix
// before
ghidra_trace_putmem(addr, length) # RuntimeError: No transaction
// after
ghidra_trace_tx_start('record mem')
ghidra_trace_putmem(addr, length)
ghidra_trace_tx_commit() Defensive patterns
Strategy: validation
Validate before calling
from ghidradbg.commands import STATE
if getattr(STATE, 'tx', None) is None:
raise RuntimeError('open a transaction first with ghidra_trace_tx_start') Type guard
def tx_is_open() -> bool:
from ghidradbg.commands import STATE
return getattr(STATE, 'tx', None) is not None and getattr(STATE, 'trace', None) is not None Try / catch
try:
ghidra_trace_putmem(addr, length)
except RuntimeError as e:
if 'No transaction' in str(e):
ghidra_trace_tx_start('retry')
ghidra_trace_putmem(addr, length)
ghidra_trace_tx_commit()
else:
raise Prevention
- Wrap every mutating command in tx_start/tx_commit.
- Use a context-manager helper so commit/abort always fires.
- Never assume a tx auto-reopens after commit.
When it happens
Trigger: Calling putmem/putmem_state/delmem/retain_values/etc. (all of which begin with STATE.require_tx()) before opening a transaction with ghidra_trace_tx_start; calling them after ghidra_trace_tx_commit or ghidra_trace_tx_abort reset STATE.tx to None; calling a write command after a trace was just started but no tx was opened.
Common situations: Script that calls ghidra_trace_start then immediately ghidra_trace_putmem, skipping the tx_start step; stale session after a commit where the developer assumed the transaction auto-reopened; copy/paste from a recipe that omitted the transaction bracket.
Related errors
- Transaction already started
- Trace already started
- No transaction
- 'ghidra_trace_connect': missing required argument 'address'
- Invalid argument: {key_list[0]}
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/66513597f6ffa17f.
Report an issue: GitHub.