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 fileView on GitHub (pinned to f906a5f850)
Solutions
- Materialize query results (ToList()/ToArray()) before the transaction is committed or disposed.
- Keep all read/write work inside the transaction's using-scope.
- Avoid reusing a transaction object after Commit; start a new one if more work is needed.
- 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
- Never enumerate a query lazily after its transaction ends.
- Scope all work inside the transaction using-block.
- Do not reuse a transaction object after Commit.
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.