{"record":{"id":"5779bd3f540ace8b","repo":"tursodatabase/turso","slug":"transaction-dropped-unexpectedly-5779bd","errorCode":null,"errorMessage":"Transaction dropped unexpectedly.","messagePattern":"Transaction dropped unexpectedly\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"bindings/rust/src/transaction.rs","lineNumber":214,"sourceCode":"        self._finish().await\n    }\n\n    #[inline]\n    async fn _finish(&mut self) -> Result<()> {\n        if self.conn.is_autocommit()? {\n            return Ok(());\n        }\n        match self.drop_behavior() {\n            DropBehavior::Commit => {\n                if (self._commit().await).is_err() {\n                    self._rollback().await\n                } else {\n                    Ok(())\n                }\n            }\n            DropBehavior::Rollback => self._rollback().await,\n            DropBehavior::Ignore => Ok(()),\n            DropBehavior::Panic => panic!(\"Transaction dropped unexpectedly.\"),\n        }\n    }\n}\n\nimpl Deref for Transaction<'_> {\n    type Target = Connection;\n\n    #[inline]\n    fn deref(&self) -> &Connection {\n        self.conn\n    }\n}\n\nimpl Drop for Transaction<'_> {\n    #[inline]\n    fn drop(&mut self) {\n        if self.in_progress {\n            self.conn","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/rust/src/transaction.rs#L196-L232","documentation":"Transaction::_finish (reached via tx.finish().await; Drop records the same behavior for later) panics when the transaction is still open (connection not in autocommit) and DropBehavior::Panic is set. It is the explicit, awaitable counterpart of the dangling-transaction panic: the caller asked to be told loudly about an unfinished transaction.","triggerScenarios":"tx.set_drop_behavior(DropBehavior::Panic) followed by tx.finish().await (or dropping tx) without a prior commit()/rollback(); also fires when commit failed, leaving the transaction open, and finish() is then called as cleanup.","commonSituations":"Strict error-handling wrappers that call finish() unconditionally; refactors that moved commit() behind a conditional branch; early-return paths inside the transaction scope.","solutions":["Call tx.commit().await or tx.rollback().await explicitly and only then finish or drop","Branch on your own success flag instead of relying on finish() while Panic is set","Use DropBehavior::Commit or Rollback if finish() is your universal cleanup path"],"exampleFix":"// before\nlet mut tx = conn.begin().await?;\ntx.set_drop_behavior(DropBehavior::Panic);\ndo_work(&tx).await?; // error path skips commit\ntx.finish().await?; // panic: transaction still open\n\n// after\nlet tx = conn.begin().await?;\nmatch do_work(&tx).await {\n    Ok(v) => { tx.commit().await?; Ok(v) }\n    Err(e) => { tx.rollback().await?; Err(e) }\n}","handlingStrategy":"validation","validationCode":"// never call finish() with Panic set on an open transaction\nif !conn.is_autocommit()? {\n    tx.commit().await?; // or rollback, per your outcome flag\n}\ntx.finish().await?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["finish() is cleanup, not commit; with Panic set it is an assertion that fires","Pair begin/commit/rollback lexically so finish() is never reached on an open transaction"],"tags":["rust","transaction","drop-behavior","panic"],"backgroundTag":"transaction-leak-detected","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}