NationalSecurityAgency/ghidra · error · RuntimeError
No transaction
Error message
No transaction
What it means
Raised as RuntimeError by State.require_tx() when STATE.tx is None — i.e. the command is executing outside an open transaction. All trace mutations must occur inside an 'open_tx' context; without one there is no transaction to commit.
Source
Thrown at Ghidra/Debug/Debugger-agent-drgn/src/main/py/src/ghidradrgn/commands.py:133
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 the mutation in `with trace.open_tx('label'):` so STATE.tx is set during the call.
- Guard: if STATE.tx is None: open a transaction first.
- Use commands.open_tracked_tx() helper which manages the tx lifecycle.
Example fix
// before
trace.create_object(...) # require_tx() -> RuntimeError
// after
with trace.open_tx('my op'):
trace.create_object(...) Defensive patterns
Strategy: validation
Validate before calling
trace = STATE.require_trace()
if STATE.tx is None:
raise RuntimeError('open a transaction first via trace.open_tx(...)') Type guard
def in_transaction() -> bool:
return getattr(STATE, 'tx', None) is not None Try / catch
try:
trace, tx = STATE.require_tx()
except RuntimeError as e:
if 'No transaction' in str(e):
with trace.open_tx('auto'):
do_mutation(trace)
return
raise Prevention
- Wrap all trace mutations in `with trace.open_tx('label'):`.
- Use commands.open_tracked_tx() to manage tx lifecycle.
- Centralize mutation entry points so a tx is always open.
When it happens
Trigger: Calling a mutating trace command outside a `with STATE.require_trace().open_tx(...)` block; after a transaction was reset/closed.
Common situations: Custom agent code mutating the trace without opening a tx; a tx that errored and was reset; mis-ordered refresh logic.
Related errors
AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14).
Data as JSON: /api/errors/39a7980af784a1c6.
Report an issue: GitHub.