litedb-org/LiteDB · error · LiteException
0
0
Error message
This thread contains an open cursors/query. Close cursors before Begin()
What it means
Thrown by LiteEngine.BeginTrans() when the calling thread already has one or more open cursors (active query readers). LiteDB creates transactions per-thread and tracks all running cursors on that transaction; it refuses to enter an explicit transaction while a reader is still being iterated because the cursor's lifecycle is tied to the transaction it was created under. The guard prevents a deadlock-prone or inconsistent state where an explicit transaction boundary overlaps an in-flight cursor.
Source
Thrown at LiteDB/Engine/Engine/Transaction.cs:23
using static LiteDB.Constants;
namespace LiteDB.Engine
{
public partial class LiteEngine
{
/// <summary>
/// Initialize a new transaction. Transaction are created "per-thread". There is only one single transaction per thread.
/// Return true if transaction was created or false if current thread already in a transaction.
/// </summary>
public bool BeginTrans()
{
_state.Validate();
var transacion = _monitor.GetTransaction(true, false, out var isNew);
transacion.ExplicitTransaction = true;
if (transacion.OpenCursors.Count > 0) throw new LiteException(0, "This thread contains an open cursors/query. Close cursors before Begin()");
LOG(isNew, $"begin trans", "COMMAND");
return isNew;
}
/// <summary>
/// Persist all dirty pages into LOG file
/// </summary>
public bool Commit()
{
_state.Validate();
var transaction = _monitor.GetTransaction(false, false, out _);
if (transaction != null)
{
// do not accept explicit commit transaction when contains open cursors runningView on GitHub (pinned to f906a5f850)
Solutions
- Dispose all open BsonDataReader/IBsonDataReader instances on the current thread before calling BeginTrans().
- Materialize query results with ToList()/ToArray() before starting a transaction.
- Wrap readers in using-statements so they close before BeginTrans() is reached.
- Reconsider whether you need an explicit transaction at all — most single operations auto-create and commit their own transaction.
Example fix
// before
var reader = db.Execute("SELECT $ FROM items");
reader.Read(); // cursor still open
db.BeginTrans(); // throws
// after
using (var reader = db.Execute("SELECT $ FROM items"))
{
while (reader.Read()) { /* consume fully */ }
}
db.BeginTrans(); Defensive patterns
Strategy: validation
Validate before calling
// No public API exposes open cursor count; enforce discipline at the call site:
// Ensure no reader is live before BeginTrans.
// Track readers explicitly in a scope object.
public sealed class DbScope : IDisposable
{
private readonly LiteDatabase _db;
private int _openReaders;
public DbScope(LiteDatabase db) => _db = db;
public IDisposable ReadScope()
{
_openReaders++;
return new Closer(() => _openReaders--);
}
public void BeginTransaction()
{
if (_openReaders > 0)
throw new InvalidOperationException($"Cannot BeginTrans: {_openReaders} reader(s) open. Dispose them first.");
_db.BeginTrans();
}
public void Dispose() => _db.Dispose();
private sealed class Closer : IDisposable
{
private readonly Action _onDispose;
public Closer(Action onDispose) => _onDispose = onDispose;
public void Dispose() => _onDispose();
}
} Try / catch
try
{
db.BeginTrans();
}
catch (LiteException ex) when (ex.Message.Contains("open cursors/query"))
{
// Dispose tracked readers, then retry once, or surface a clear error.
throw new InvalidOperationException("Close all open readers before starting a transaction.", ex);
} Prevention
- Always wrap BsonDataReader in a using-statement.
- Materialize query results before calling BeginTrans().
- Prefer auto-transactions for single operations to avoid manual lifecycle bugs.
- Never iterate a reader lazily across a BeginTrans() boundary.
When it happens
Trigger: Calling db.BeginTrans() while a previously opened IBsonDataReader (from db.Execute(string) or ILiteCollection.Query().ToEnumerable()) on the same thread has not yet been disposed. Happens when a developer iterates a reader lazily and calls BeginTrans() mid-iteration.
Common situations: Mixing manual transaction management with deferred query execution; using LINQ over a reader without disposing it first; foreach over a query result where the body calls BeginTrans().
Related errors
- 0
- Timed out waiting for ReleaseTransaction to finish.
- Expected LiteException was not observed.
- LiteException did not contain expected message. Actual: {obs
- Failed to begin primary transaction for reproduction.
AI-assisted analysis of litedb-org/LiteDB@f906a5f850 (2026-08-13).
Data as JSON: /api/errors/7e914d3c088a221b.
Report an issue: GitHub.