{"record":{"id":"e980655d01132ecd","repo":"dotnet/orleans","slug":"recovered-name-count-does-not-match-written-w","errorCode":null,"errorMessage":"Recovered {name} count does not match. Written: {written.Count}, recovered: {recovered.Count}.","messagePattern":"Recovered (.+?) count does not match\\. Written: (.+?), recovered: (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"samples/JournalingAzureBlobJson/JournalingAzureBlobJson/Program.cs","lineNumber":124,"sourceCode":"        {\n            throw new InvalidOperationException(\"The grain did not reactivate after DeactivateOnIdle.\");\n        }\n\n        EnsureEqual(\"inventory\", written.Inventory, recovered.Inventory, InventoryEntryEquals);\n        EnsureEqual(\"events\", written.Events, recovered.Events, JournalEventEquals);\n        EnsureEqual(\"work queue\", written.WorkQueue, recovered.WorkQueue, WorkItemEquals);\n        EnsureEqual(\"tags\", written.Tags, recovered.Tags, static (left, right) => string.Equals(left, right, StringComparison.Ordinal));\n        EnsureEqual(\"balance\", written.Balance, recovered.Balance, AccountBalanceEquals);\n        EnsureEqual(\"profile\", written.Profile, recovered.Profile, ProfileStateEquals);\n        EnsureEqual(\"completion status\", written.CompletionStatus, recovered.CompletionStatus, static (left, right) => left == right);\n        EnsureEqual(\"receipt\", written.Receipt, recovered.Receipt, static (left, right) => left == right);\n    }\n\n    private static void EnsureEqual<T>(string name, IReadOnlyList<T> written, IReadOnlyList<T> recovered, Func<T, T, bool> equals)\n    {\n        if (written.Count != recovered.Count)\n        {\n            throw new InvalidOperationException($\"Recovered {name} count does not match. Written: {written.Count}, recovered: {recovered.Count}.\");\n        }\n\n        for (var i = 0; i < written.Count; i++)\n        {\n            if (!equals(written[i], recovered[i]))\n            {\n                throw new InvalidOperationException($\"Recovered {name} item {i} does not match. Written: {Serialize(written[i])}, recovered: {Serialize(recovered[i])}.\");\n            }\n        }\n    }\n\n    private static void EnsureEqual<T>(string name, T written, T recovered, Func<T, T, bool> equals)\n    {\n        if (!equals(written, recovered))\n        {\n            throw new InvalidOperationException($\"Recovered {name} does not match. Written: {Serialize(written)}, recovered: {Serialize(recovered)}.\");\n        }\n    }","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/samples/JournalingAzureBlobJson/JournalingAzureBlobJson/Program.cs#L106-L142","documentation":"An InvalidOperationException thrown by the list-overload of EnsureEqual during recovery validation when a recovered durable collection (inventory, events, work queue, tags) has a different element count than what was written. It indicates the journal was not replayed completely or an entry was lost/added during the write-recover cycle. The message names the collection and both counts.","triggerScenarios":"After deactivation+recovery, written.<collection>.Count != recovered.<collection>.Count for one of the validated lists (EnsureEqual is called on Inventory, Events, WorkQueue, Tags). The first divergence detected throws.","commonSituations":"A journaling bug (entries not flushed before deactivation, checkpoint/WAL truncation), a serialization mismatch dropping items, or premature deactivation before WriteStateAsync completed. A schema change between write and recovery.","solutions":["Ensure WriteStateAsync() has completed before deactivating the grain (the sample calls it at the end of RunScenario).","Inspect the raw WAL blob (printed by the sample) to confirm all journal entries are present and valid JSONL.","Check the durable-collection codec/source generation — a missing [GenerateSerializer]/[JsonSerializable] can silently drop items."],"exampleFix":"// before (no guard that state flushed)\nreturn CreateSummary();\n\n// after (explicit flush before summarizing)\nawait WriteStateAsync();\nreturn CreateSummary();","handlingStrategy":"validation","validationCode":"static void RequireCount<T>(string name, IReadOnlyList<T> written, IReadOnlyList<T> recovered) {\n    if (written.Count != recovered.Count)\n        throw new InvalidOperationException($\"{name} count mismatch: {written.Count} vs {recovered.Count}\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure WriteStateAsync() completes before deactivation so the journal is fully flushed.","Inspect the raw WAL/JSONL blob to confirm all entries persisted.","Keep serialization attributes and source-gen complete so no entries are dropped."],"tags":["orleans","journaling","durability","test-assertion","data-integrity"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}