{"record":{"id":"55696140f2b4d065","repo":"litedb-org/LiteDB","slug":"timed-out-waiting-for-releasetransaction-to-finish","errorCode":null,"errorMessage":"Timed out waiting for ReleaseTransaction to finish.","messagePattern":"Timed out waiting for ReleaseTransaction to finish\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"warning","filePath":"LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs","lineNumber":137,"sourceCode":"                observedException = liteException;\n                Log(host, \"Observed LiteException from ReleaseTransaction on worker thread:\");\n                Log(host, liteException.ToString());\n            }\n            finally\n            {\n                reproObserved.Set();\n            }\n        })\n        {\n            IsBackground = true,\n            Name = \"Issue2561-Repro-Worker\"\n        };\n\n        worker.Start();\n\n        if (!reproObserved.Wait(TimeSpan.FromSeconds(10)))\n        {\n            throw new TimeoutException(\"Timed out waiting for ReleaseTransaction to finish.\");\n        }\n\n        rollback.Invoke(engine, Array.Empty<object>());\n\n        if (observedException is null)\n        {\n            throw new InvalidOperationException(\"Expected LiteException was not observed.\");\n        }\n\n        if (!observedException.Message.Contains(\"current thread must contains transaction parameter\", StringComparison.OrdinalIgnoreCase))\n        {\n            throw new InvalidOperationException($\"LiteException did not contain expected message. Actual: {observedException.Message}\");\n        }\n\n        Log(host, \"Repro succeeded: ReleaseTransaction threw on the wrong thread.\");\n    }\n\n    private static void Log(ReproHostClient host, string message, ReproHostLogLevel level = ReproHostLogLevel.Information)","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/litedb-org/LiteDB/blob/f906a5f850678719e39a39a006cb66dcae563cfa/LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs#L119-L155","documentation":"Thrown by the Issue 2561 repro harness when a background worker thread does not signal completion within a 10-second window. The worker reflects into the LiteEngine's TransactionMonitor and calls ReleaseTransaction on a captured transaction; if that reflection path deadlocks or hangs, reproObserved.Wait returns false. It is a test-harness timeout, not a normal LiteDB runtime error.","triggerScenarios":"Calling ReleaseTransaction via reflection on a thread that does not own the transaction while the main thread holds the transaction open, causing the monitor to block or the reflected invoke to never unwind within 10s. Reproduces only when the TransactionMonitor refuses to release a transaction from a foreign thread by blocking instead of throwing.","commonSituations":"Running the Issue_2561 repro on a build where the transaction monitor's thread-affinity behavior changed (e.g. it now blocks on a lock rather than throwing), under heavy thread contention, or in a debugger that pauses the worker thread past the 10s budget.","solutions":["Increase the timeout passed to reproObserved.Wait if running under a debugger or slow CI.","Confirm the LiteDB build under test still throws LiteException from ReleaseTransaction on the wrong thread; if the fix removed that path the repro is obsolete.","Run the repro outside of an attached debugger to avoid thread suspension skewing the 10s budget.","If the worker truly hangs, capture a dump of both threads to inspect the TransactionMonitor lock state."],"exampleFix":"// before\nif (!reproObserved.Wait(TimeSpan.FromSeconds(10)))\n{\n    throw new TimeoutException(\"Timed out waiting for ReleaseTransaction to finish.\");\n}\n\n// after\nif (!reproObserved.Wait(TimeSpan.FromSeconds(60)))\n{\n    throw new TimeoutException(\"Timed out waiting for ReleaseTransaction to finish. \" +\n        $\"Worker alive={worker.IsAlive}.\");\n}","handlingStrategy":"retry","validationCode":"// Before the repro, assert the engine still owns the transaction on the main thread\nvar transactions = (IEnumerable<object>)(transactionsProperty.GetValue(monitor));\nif (transactions == null || !transactions.Any())\n{\n    throw new InvalidOperationException(\"No active transaction to release; repro preconditions not met.\");\n}","typeGuard":null,"tryCatchPattern":"// Repro harness: treat a hang as a soft failure, not a hard crash\nManualResetEventSlim done = new(false);\n// ... worker sets done ...\nif (!done.Wait(TimeSpan.FromSeconds(30)))\n{\n    Log(host, \"ReleaseTransaction did not finish in time; skipping assertion.\", ReproHostLogLevel.Warning);\n    return; // or mark the repro inconclusive\n}","preventionTips":["Size the timeout to the slowest expected environment (debugger, CI, contention).","Log worker.IsAlive on timeout to distinguish a hang from a slow completion.","Validate reflection targets (fields/methods) exist before invoking so the worker cannot no-op silently."],"tags":["transaction","repro","timeout","threading","reflection"],"backgroundTag":null,"analyzedSha":"f906a5f850678719e39a39a006cb66dcae563cfa","analyzedAt":"2026-08-13T21:56:30.148Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}