{"record":{"id":"4b3386c29ab52f18","repo":"dotnet/orleans","slug":"balance-has-not-been-written","errorCode":null,"errorMessage":"Balance has not been written.","messagePattern":"Balance has not been written\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"samples/JournalingAzureBlobJson/JournalingAzureBlobJson/Program.cs","lineNumber":317,"sourceCode":"\n    public Task Deactivate()\n    {\n        DeactivateOnIdle();\n        return Task.CompletedTask;\n    }\n\n    private JournaledSampleSummary CreateSummary()\n    {\n        var completion = receipt.State;\n        return new JournaledSampleSummary(\n            _activationId,\n            inventory.OrderBy(static item => item.Key, StringComparer.Ordinal)\n                .Select(static item => new InventoryEntry(item.Key, item.Value))\n                .ToArray(),\n            events.ToArray(),\n            workQueue.ToArray(),\n            tags.Order(StringComparer.Ordinal).ToArray(),\n            balance.Value ?? throw new InvalidOperationException(\"Balance has not been written.\"),\n            profile.State,\n            completion.Status,\n            completion.Value);\n    }\n}\n\n[GenerateSerializer]\npublic sealed record JournaledSampleSummary(\n    [property: Id(0)] Guid ActivationId,\n    [property: Id(1)] InventoryEntry[] Inventory,\n    [property: Id(2)] JournalEvent[] Events,\n    [property: Id(3)] WorkItem[] WorkQueue,\n    [property: Id(4)] string[] Tags,\n    [property: Id(5)] AccountBalance Balance,\n    [property: Id(6)] ProfileState Profile,\n    [property: Id(7)] DurableTaskCompletionSourceStatus CompletionStatus,\n    [property: Id(8)] Receipt? Receipt);\n","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/samples/JournalingAzureBlobJson/JournalingAzureBlobJson/Program.cs#L299-L335","documentation":"Thrown inside CreateSummary when balance.Value is null — the IDurableValue<AccountBalance> durable value was never assigned before the summary is built. CreateSummary is called from both RunScenario (which sets balance.Value first) and GetSummary, so calling GetSummary on an activation that never ran the scenario trips it.","triggerScenarios":"A client calls grain.GetSummary() on a fresh activation (or one whose RunScenario was never invoked), so balance.Value is still its default null. CreateSummary evaluates `balance.Value ?? throw new InvalidOperationException(...)` and the null-coalesce fires.","commonSituations":"Restarting the silo against an empty/cleared blob and querying the summary before running the scenario; a second client calling GetSummary while the first has not finished RunScenario; changing the sample flow so balance is written conditionally.","solutions":["Call grain.RunScenario() first — it sets balance.Value before WriteStateAsync — then call GetSummary.","If you want GetSummary to be safe on cold activations, guard with `if (balance.Value is null) return ...` or return an empty/error summary instead of throwing.","Ensure the append blob was not reset between RunScenario and GetSummary, otherwise recovery yields a null balance."],"exampleFix":"// before\nvar summary = await grain.GetSummary(); // throws on cold activation\n\n// after: run the scenario that initializes balance before summarizing\nawait grain.RunScenario();\nvar summary = await grain.GetSummary();","handlingStrategy":"validation","validationCode":"// Guard GetSummary-style calls against an unwritten balance\nvar summary = (balance.Value is null)\n    ? null\n    : CreateSummary();","typeGuard":"// Narrow a nullable durable value before use\nstatic bool TryGetBalance(IDurableValue<AccountBalance> balance, out AccountBalance value)\n{\n    value = balance.Value!;\n    return value is not null;\n}","tryCatchPattern":"null","preventionTips":["Call RunScenario before GetSummary so balance.Value is populated.","Prefer null-safe summary construction over throwing from a getter.","Do not clear the append blob between RunScenario and GetSummary."],"tags":["journaling","nullable","lifecycle","sample","state"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}