litedb-org/LiteDB · error · LiteException
0
0
Error message
There is no more active transaction for this cursor: {_cursor.Query.ToSQL(_cursor.Collection)} What it means
Thrown during cursor iteration in QueryExecutor when the transaction backing the cursor is no longer in the Active state between yielding documents. This typically means the transaction was committed, rolled back, or disposed while the cursor (BsonDataReader) was still open and being enumerated. The engine checks transaction.State != Active after each yielded document and aborts to prevent reading from a dead snapshot.
Source
Thrown at LiteDB/Engine/Query/QueryExecutor.cs:149
try
{
read = enumerator.MoveNext();
}
catch (Exception ex)
{
_state.Handle(ex);
throw ex;
}
while (read)
{
_cursor.Fetched++;
_cursor.Elapsed.Stop();
yield return enumerator.Current;
if (transaction.State != TransactionState.Active) throw new LiteException(0, $"There is no more active transaction for this cursor: {_cursor.Query.ToSQL(_cursor.Collection)}");
_cursor.Elapsed.Start();
try
{
read = enumerator.MoveNext();
}
catch (Exception ex)
{
_state.Handle(ex);
throw ex;
}
}
}
};
}
/// <summary>View on GitHub (pinned to f906a5f850)
Solutions
- Fully enumerate and dispose the reader before committing, rolling back, or disposing the database.
- Materialize results (ToList/ToArray) if the reader must outlive the transaction.
- Avoid mixing explicit Commit/Rollback with an open reader on the same thread.
- Increase the TIMEOUT pragma if the cursor legitimately runs long, or paginate.
Example fix
// before — reader iterated after dispose/commit
IEnumerable<BsonDocument> GetDocs()
{
using var db = new LiteDatabase("data.db");
var reader = db.Execute("SELECT $ FROM items");
while (reader.Read()) yield return reader.Current.AsDocument;
} // db disposed while lazy yield still active -> throws
// after — materialize before dispose
List<BsonDocument> GetDocs()
{
using var db = new LiteDatabase("data.db");
var result = new List<BsonDocument>();
using var reader = db.Execute("SELECT $ FROM items");
while (reader.Read()) result.Add(reader.Current.AsDocument);
return result;
} Defensive patterns
Strategy: validation
Validate before calling
public List<BsonDocument> SafeQuery(LiteDatabase db, string sql)
{
var result = new List<BsonDocument>();
using (var reader = db.Execute(sql))
{
while (reader.Read())
result.Add(reader.Current.AsDocument);
} // reader disposed before db goes out of scope
return result;
} Try / catch
try
{
foreach (var doc in LazyQuery(db)) { /* ... */ }
}
catch (LiteException ex) when (ex.Message.Contains("no more active transaction"))
{
// The reader outlived its transaction. Materialize results earlier and retry.
throw new InvalidOperationException("Reader was enumerated after its transaction ended. Call ToList() before the transaction closes.", ex);
} Prevention
- Fully enumerate and dispose readers before committing or disposing the DB.
- Never return a lazy BsonDataReader from a scope that will be disposed.
- Materialize with ToList()/ToArray() if results must outlive the transaction.
- Keep reader lifetimes strictly shorter than the transaction.
When it happens
Trigger: Opening a reader from db.Execute/Query, then calling db.Commit(), db.Rollback(), or disposing the LiteDatabase/engine before fully iterating the reader. Also occurs if the transaction monitor evicts/times out the transaction mid-cursor due to lock timeout or Safepoint abort.
Common situations: Returning a lazy BsonDataReader from a using-block scope and enumerating it after disposal; committing inside a foreach over a query result; long-running cursor that exceeds the lock timeout; nested transactions or auto-commit interfering.
Related errors
AI-assisted analysis of litedb-org/LiteDB@f906a5f850 (2026-08-13).
Data as JSON: /api/errors/abca8356d09fc48a.
Report an issue: GitHub.