litedb-org/LiteDB · error · LiteException

0

0

Error message

This transaction are invalid state

What it means

Thrown by TransactionService.Safepoint when the transaction's _state is not TransactionState.Active (i.e. already Committed or Aborted). Safepoint runs periodically to flush dirty pages when a transaction holds too many pages; calling any operation that triggers it after the transaction ended hits this guard.

Source

Thrown at LiteDB/Engine/Services/TransactionService.cs:124

            }
            else
            {
                // if not exits, let's create here
                _snapshots[collection] = snapshot = create();
            }

            // update transaction mode to write in first write snaphost request
            if (mode == LockMode.Write) _mode = LockMode.Write;

            return snapshot;
        }

        /// <summary>
        /// If current transaction contains too much pages, now is safe to remove clean pages from memory and flush to wal disk dirty pages
        /// </summary>
        public void Safepoint()
        {
            if (_state != TransactionState.Active) throw new LiteException(0, "This transaction are invalid state");

            if (_monitor.CheckSafepoint(this))
            {
                LOG($"safepoint flushing transaction pages: {_transPages.TransactionSize}", "TRANSACTION");

                // if any snapshot are writable, persist pages
                if (_mode == LockMode.Write)
                {
                    this.PersistDirtyPages(false);
                }

                // clear local pages in all snapshots (read/write snapshosts)
                foreach (var snapshot in _snapshots.Values)
                {
                    snapshot.Clear();
                }

                // there is no local pages in cache and all dirty pages are in log file

View on GitHub (pinned to f906a5f850)

Solutions

  1. Materialize query results (ToList()/ToArray()) before the transaction is committed or disposed.
  2. Keep all read/write work inside the transaction's using-scope.
  3. Avoid reusing a transaction object after Commit; start a new one if more work is needed.
  4. Catch and handle earlier exceptions so a transaction is not silently left half-aborted while later code keeps using it.

Example fix

// before
List<BsonDocument> docs;
using (var tx = db.BeginTransaction()) {
    docs = tx.GetCollection("c").FindAll(); // lazy
    tx.Commit();
}
foreach (var d in docs) { } // materializes after tx ended -> throws

// after
using (var tx = db.BeginTransaction()) {
    var docs = tx.GetCollection("c").FindAll().ToList();
    tx.Commit();
    Process(docs);
}
Defensive patterns

Strategy: validation

Validate before calling

// materialize inside the transaction scope
using (var tx = db.BeginTransaction()) {
    var rows = tx.GetCollection("c").FindAll().ToList(); // eager
    tx.Commit();
    return rows; // safe to use after commit
}

Try / catch

try { /* operation that may trigger Safepoint */ }
catch (LiteException ex) when (ex.Message.Contains("invalid state")) {
    // transaction already finished: start a fresh one and redo
}

Prevention

When it happens

Trigger: Continuing to use a transaction (or a cursor/snapshot derived from it) after Commit() or after it was auto-aborted; a deferred/lazy query (IEnumerable yield) that materializes after the transaction was disposed.

Common situations: Returning an IQueryable/IEnumerable from a using-block that disposed the transaction; lazy query enumeration outside the transaction scope; double-commit; long-running LINQ over documents whose safepoint fires after the tx was aborted by an earlier error.

Related errors


AI-assisted analysis of litedb-org/LiteDB@f906a5f850 (2026-08-13). Data as JSON: /api/errors/f73d23568cd780fc. Report an issue: GitHub.