{"record":{"id":"6503ff154a5f4067","repo":"litedb-org/LiteDB","slug":"liteexception-did-not-contain-expected-message-ac","errorCode":null,"errorMessage":"LiteException did not contain expected message. Actual: {observedException.Message}","messagePattern":"LiteException did not contain expected message\\. Actual: (.+?)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs","lineNumber":149,"sourceCode":"        };\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        }\n        else\n        {\n            Console.WriteLine(message);\n        }\n    }","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/litedb-org/LiteDB/blob/f906a5f850678719e39a39a006cb66dcae563cfa/LiteDB.ReproRunner/Repros/Issue_2561_TransactionMonitor/Program.cs#L131-L167","documentation":"Thrown by the Issue 2561 repro harness when a LiteException was observed from ReleaseTransaction but its message did not contain 'current thread must contains transaction parameter'. The repro asserts not just that an exception fired, but that it carries the specific thread-affinity diagnostic. A different message means the failure mode shifted from the documented one.","triggerScenarios":"ReleaseTransaction threw a LiteException on the wrong thread, but with a different message (e.g. a 'transaction not found', 'transaction already released', or a localized/altered string). Happens when the exception text was edited, or when the captured transaction was already released and the monitor throws a different error before reaching the thread-affinity check.","commonSituations":"LiteDB version change that reworded the thread-affinity exception message, or a repro ordering change where the main thread already rolled back before the worker invoked ReleaseTransaction, causing a stale-transaction error instead.","solutions":["Read observedException.Message from the log to see the actual failure, then reconcile it with the expected thread-affinity text.","Update the expected substring to match the current LiteDB message if the wording was intentionally changed.","Ensure the main thread holds the transaction open (no early Rollback/Commit) until after the worker invokes ReleaseTransaction.","Confirm the repro uses the same LiteDB build whose message string it asserts against."],"exampleFix":"// before\nif (!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// after\nvar expectedFragments = new[] { \"current thread must contains transaction parameter\", \"transaction parameter\" };\nif (!expectedFragments.Any(f => observedException.Message.Contains(f, StringComparison.OrdinalIgnoreCase)))\n{\n    throw new InvalidOperationException($\"LiteException did not contain expected message. Actual: {observedException.Message}\");\n}","handlingStrategy":"validation","validationCode":"// Relax the assertion to a set of known-good fragments\nvar acceptableFragments = new[] { \"current thread must contains transaction parameter\", \"transaction parameter\", \"wrong thread\" };\nbool ok = observedException != null && acceptableFragments.Any(f => observedException.Message.Contains(f, StringComparison.OrdinalIgnoreCase));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not assert on exact exception wording; match against the semantic invariant.","Log the full observedException.ToString() so the actual message is recoverable.","Keep the expected-message list in sync with the LiteDB version under test."],"tags":["transaction","repro","assertion","threading","exception-message"],"backgroundTag":null,"analyzedSha":"f906a5f850678719e39a39a006cb66dcae563cfa","analyzedAt":"2026-08-13T21:56:30.148Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}