{"record":{"id":"39a7980af784a1c6","repo":"NationalSecurityAgency/ghidra","slug":"no-transaction-39a798","errorCode":null,"errorMessage":"No transaction","messagePattern":"No transaction","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-agent-drgn/src/main/py/src/ghidradrgn/commands.py","lineNumber":133,"sourceCode":"\n    def require_trace(self) -> Trace[Extra]:\n        if self.trace is None:\n            raise RuntimeError(\"No trace active\")\n        return self.trace\n\n    def require_no_trace(self) -> None:\n        if self.trace != None:\n            raise RuntimeError(\"Trace already started\")\n\n    def reset_trace(self) -> None:\n        self.trace: Optional[Trace[Extra]] = None\n        util.set_convenience_variable('_ghidra_tracing', \"false\")\n        self.reset_tx()\n\n    def require_tx(self) -> Tuple[Trace, Transaction]:\n        trace = self.require_trace()\n        if self.tx is None:\n            raise RuntimeError(\"No transaction\")\n        return trace, self.tx\n\n    def require_no_tx(self) -> None:\n        if self.tx != None:\n            raise RuntimeError(\"Transaction already started\")\n\n    def reset_tx(self) -> None:\n        self.tx: Optional[Transaction] = None\n\n\nSTATE = State()\n\n\ndef ghidra_trace_connect(address: Optional[str] = None) -> None:\n    \"\"\"\n    Connect Python to Ghidra for tracing\n\n    Address must be of the form 'host:port'","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-agent-drgn/src/main/py/src/ghidradrgn/commands.py#L115-L151","documentation":"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.","triggerScenarios":"Calling a mutating trace command outside a `with STATE.require_trace().open_tx(...)` block; after a transaction was reset/closed.","commonSituations":"Custom agent code mutating the trace without opening a tx; a tx that errored and was reset; mis-ordered refresh logic.","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."],"exampleFix":"// before\ntrace.create_object(...)  # require_tx() -> RuntimeError\n// after\nwith trace.open_tx('my op'):\n    trace.create_object(...)","handlingStrategy":"validation","validationCode":"trace = STATE.require_trace()\nif STATE.tx is None:\n    raise RuntimeError('open a transaction first via trace.open_tx(...)')","typeGuard":"def in_transaction() -> bool:\n    return getattr(STATE, 'tx', None) is not None","tryCatchPattern":"try:\n    trace, tx = STATE.require_tx()\nexcept RuntimeError as e:\n    if 'No transaction' in str(e):\n        with trace.open_tx('auto'):\n            do_mutation(trace)\n        return\n    raise","preventionTips":["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."],"tags":["drgn","transaction","state-machine","precondition"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}