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.conn

View on GitHub (pinned to c1e5928725)

Solutions

  1. Call tx.commit().await or tx.rollback().await explicitly and only then finish or drop
  2. Branch on your own success flag instead of relying on finish() while Panic is set
  3. 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

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


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20). Data as JSON: /api/errors/5779bd3f540ace8b. Report an issue: GitHub.