tursodatabase/turso · error
Transaction dropped unexpectedly.
Error message
Transaction dropped unexpectedly.
What it means
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.
Source
Thrown at bindings/rust/src/transaction.rs:214
self._finish().await
}
#[inline]
async fn _finish(&mut self) -> Result<()> {
if self.conn.is_autocommit()? {
return Ok(());
}
match self.drop_behavior() {
DropBehavior::Commit => {
if (self._commit().await).is_err() {
self._rollback().await
} else {
Ok(())
}
}
DropBehavior::Rollback => self._rollback().await,
DropBehavior::Ignore => Ok(()),
DropBehavior::Panic => panic!("Transaction dropped unexpectedly."),
}
}
}
impl Deref for Transaction<'_> {
type Target = Connection;
#[inline]
fn deref(&self) -> &Connection {
self.conn
}
}
impl Drop for Transaction<'_> {
#[inline]
fn drop(&mut self) {
if self.in_progress {
self.connView on GitHub (pinned to c1e5928725)
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
Example fix
// before
let mut tx = conn.begin().await?;
tx.set_drop_behavior(DropBehavior::Panic);
do_work(&tx).await?; // error path skips commit
tx.finish().await?; // panic: transaction still open
// after
let tx = conn.begin().await?;
match do_work(&tx).await {
Ok(v) => { tx.commit().await?; Ok(v) }
Err(e) => { tx.rollback().await?; Err(e) }
} Defensive patterns
Strategy: validation
Validate before calling
// never call finish() with Panic set on an open transaction
if !conn.is_autocommit()? {
tx.commit().await?; // or rollback, per your outcome flag
}
tx.finish().await?; Prevention
- 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
When it happens
Trigger: 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.
Common situations: Strict error-handling wrappers that call finish() unconditionally; refactors that moved commit() behind a conditional branch; early-return paths inside the transaction scope.
Related errors
- Transaction dropped unexpectedly.
- failed to generate random bytes
- Invalid drop behavior: {value}
- Could not determine home directory
- Error setting Ctrl-C handler
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20).
Data as JSON: /api/errors/5779bd3f540ace8b.
Report an issue: GitHub.