{"record":{"id":"a2d38b4319b59931","repo":"litedb-org/LiteDB","slug":"expected-liteexception-was-not-observed","errorCode":null,"errorMessage":"Expected LiteException was not observed.","messagePattern":"Expected LiteException was not observed\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs","lineNumber":144,"sourceCode":"            }\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)\n    {\n        host.SendLog(message, level);\n\n        if (level >= ReproHostLogLevel.Warning)\n        {\n            Console.Error.WriteLine(message);\n        }","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/litedb-org/LiteDB/blob/f906a5f850678719e39a39a006cb66dcae563cfa/LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs#L126-L162","documentation":"Thrown by the Issue 2561 repro harness when the worker thread finished without capturing a LiteException. The repro asserts that calling ReleaseTransaction on a transaction owned by another thread must throw LiteException; if ReleaseTransaction completed silently, observedException stays null and this assertion fires. It signals that the expected thread-affinity guard did not trigger.","triggerScenarios":"ReleaseTransaction.Invoke(monitor, capturedTransaction) returned normally on the worker thread instead of throwing, meaning the monitor allowed a foreign thread to release a transaction it did not open. Occurs on LiteDB builds where the thread-affinity check in ReleaseTransaction was removed, weakened, or short-circuited.","commonSituations":"Running the repro against a LiteDB version that already fixed or regressed the thread-affinity guard, or where the captured transaction object is no longer the active one by the time the worker runs (so ReleaseTransaction is a no-op rather than a violation).","solutions":["Verify the LiteDB version under test still enforces that ReleaseTransaction rejects calls from threads that do not own the transaction.","Inspect the host log lines emitted by the worker: 'ReleaseTransaction completed without throwing' confirms the guard did not fire.","Re-check the reflection setup: ensure capturedTransaction is the live, main-thread-owned transaction when the worker invokes ReleaseTransaction.","If the guard was intentionally removed, retire this repro rather than treating the assertion as a failure."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before asserting, confirm the worker captured something\nif (observedException is null)\n{\n    Log(host, \"ReleaseTransaction did not throw; thread-affinity guard may be absent in this build.\", ReproHostLogLevel.Warning);\n    return; // mark repro inconclusive instead of throwing","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Log both outcomes (threw vs completed) so a missing guard is observable, not just a hard failure.","Pin the LiteDB version the repro asserts against so message/behavior drift is detected at upgrade time.","Treat a non-throwing ReleaseTransaction as inconclusive in CI rather than a red failure unless the guard is required."],"tags":["transaction","repro","threading","reflection","assertion"],"backgroundTag":null,"analyzedSha":"f906a5f850678719e39a39a006cb66dcae563cfa","analyzedAt":"2026-08-13T21:56:30.148Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}