{"record":{"id":"9a2072244fd4884c","repo":"NationalSecurityAgency/ghidra","slug":"transaction-already-started-9a2072","errorCode":null,"errorMessage":"Transaction already started","messagePattern":"Transaction already started","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py","lineNumber":151,"sourceCode":"\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    \"\"\"Connect Python to Ghidra for tracing.\n\n    Address must be of the form 'host:port'\n    \"\"\"\n\n    STATE.require_no_client()\n    if address is None:\n        raise RuntimeError(\n            \"'ghidra_trace_connect': missing required argument 'address'\")","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py#L133-L169","documentation":"Raised by State.require_no_tx() (commands.py:149-151) when STATE.tx is already non-None. The x64dbg trace agent keeps a single global STATE singleton holding at most one open Transaction; require_no_tx() is a precondition guard invoked by commands that must begin a fresh transaction (e.g. trace-start paths). A second start while the first is uncommitted trips the guard.","triggerScenarios":"Calling a command that calls STATE.require_no_tx() while a previous transaction is still open: e.g. ghidra_trace_tx_start invoked twice without a matching ghidra_trace_tx_end, or ghidra_trace_start after a tx_start with no tx_end/commit. Any nested transaction attempt on the singleton STATE.","commonSituations":"A script that opens a tx, hits an error before reaching the end/commit, then retries the same command; forgetting to pair tx_start with tx_end; running two trace commands in sequence where the first left the tx open due to an exception that was swallowed.","solutions":["Close/commit the existing transaction first (call ghidra_trace_tx_end, or whichever end/commit your command provides) before starting a new one.","Call STATE.reset_tx() in a finally block so an aborted transaction never leaks into the next command.","Guard before starting: check 'if STATE.tx is not None' and end it before opening another.","Wrap each transactional command body in try/finally that ends the tx on every exit path."],"exampleFix":"// before\nghidra_trace_tx_start()\n# ... error here, tx never closed ...\nghidra_trace_tx_start()  # -> Transaction already started\n\n// after\nif STATE.tx is not None:\n    ghidra_trace_tx_end()\nghidra_trace_tx_start()","handlingStrategy":"validation","validationCode":"def safe_tx_start():\n    if STATE.tx is not None:\n        raise RuntimeError(f\"Refusing to start tx: one is open already ({STATE.tx})\")\n    # ... open the transaction ...","typeGuard":"def tx_is_open() -> bool:\n    return STATE.tx is not None","tryCatchPattern":"try:\n    ghidra_trace_tx_start()\nexcept RuntimeError as e:\n    if 'already started' in str(e):\n        ghidra_trace_tx_end()\n        ghidra_trace_tx_start()\n    else:\n        raise","preventionTips":["Always pair tx_start with tx_end inside a try/finally.","Reset STATE.tx in an exception handler so aborted commands do not leak an open tx.","Before starting, assert STATE.tx is None."],"tags":["transaction","state-machine","trace-agent","python"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}