{"record":{"id":"2c4ddb993370af70","repo":"NationalSecurityAgency/ghidra","slug":"transaction-already-started-2c4ddb","errorCode":null,"errorMessage":"Transaction already started","messagePattern":"Transaction already started","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java","lineNumber":109,"sourceCode":"\t\t\tthrow new RuntimeException(\"Trace already started\");\n\t\t}\n\t}\n\n\tpublic void resetTrace() {\n\t\ttrace = null;\n\t\tresetTx();\n\t}\n\n\tpublic RmiTransaction requireTx() {\n\t\tif (tx == null) {\n\t\t\tthrow new RuntimeException(\"No transaction\");\n\t\t}\n\t\treturn tx;\n\t}\n\n\tpublic void requireNoTx() {\n\t\tif (tx != null) {\n\t\t\tthrow new RuntimeException(\"Transaction already started\");\n\t\t}\n\t}\n\n\tpublic void resetTx() {\n\t\ttx = null;\n\t}\n\n}\n\npublic class JdiCommands {\n\n\tprivate JdiConnector connector;\n\tprivate JdiManagerImpl jdi;\n\n\tpublic State state;\n\tprivate String[] regNames = { \"PC\", \"return_address\" };\n\tpublic long MAX_REFS = 100;\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Debugger-jpda/src/main/java/ghidra/dbg/jdi/rmi/jpda/JdiCommands.java#L91-L127","documentation":"Thrown by State.requireNoTx() when a command that must run outside a transaction is invoked while a trace transaction is still open (tx != null). Ghidra's JDI trace bridge tracks a single active RmiTransaction in the State object; operations like opening a new transaction or connecting require there be none in flight. The guard prevents nested/overlapping transactions that would corrupt trace snapshot consistency.","triggerScenarios":"Calling any JdiCommands method that internally invokes state.requireNoTx() — such as starting a new transaction, connecting, or resetting — after ghidraTraceTxStart() (or equivalent) was called without a matching commit/rollback. Also occurs when a prior command threw an exception mid-transaction and resetTx() was never reached, leaving tx non-null for the next command.","commonSituations":"An earlier transaction was abandoned due to an uncaught exception in the JDI agent; the trace session was interrupted and not cleanly torn down; a script opened a transaction and forgot to call ghidraTraceTxCommit/ghidraTraceTxEnd.","solutions":["Call ghidraTraceTxCommit() or equivalent to close the open transaction, then retry the failing command.","If the transaction is stale or the session is in an unknown state, call the reset/disconnect command to tear down State and start fresh.","Audit your automation script to ensure every ghidraTraceTxStart() is paired with a commit in a try/finally block so exceptions don't leave tx dangling."],"exampleFix":"// before\nstate.requireNoTx(); // throws if prior tx leaked\n\n// after — guard the transaction lifecycle\ntry {\n    ghidraTraceTxStart(\"op\");\n    // ... work ...\n} finally {\n    ghidraTraceTxCommit(); // or ghidraTraceTxEnd()\n}","handlingStrategy":"validation","validationCode":"// Before invoking a command that calls requireNoTx(), check State\nif (state.tx != null) {\n    // commit or reset the dangling transaction first\n    ghidraTraceTxCommit(); // or state.resetTx(); if known-stale\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair ghidraTraceTxStart() with a commit/end in a try/finally so exceptions cannot leave a transaction open.","After any error in a trace session, explicitly reset State before issuing new commands.","Treat a non-null tx field as an invalid precondition and resolve it before proceeding."],"tags":["transaction","jdi","state-management","ghidra-debug"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}