{"record":{"id":"e47d8594a70fc39d","repo":"microsoft/aspire","slug":"historical-dashboard-data-is-read-only-e47d85","errorCode":null,"errorMessage":"Historical dashboard data is read-only.","messagePattern":"Historical dashboard data is read-only\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/ServiceClient/SelectedDashboardClient.cs","lineNumber":77,"sourceCode":"    }\n\n    public Task<ResourceCommandResponseViewModel> ExecuteResourceCommandAsync(string resourceName, string resourceType, CommandViewModel command, ExecuteResourceCommandOptions options, CancellationToken cancellationToken)\n    {\n        EnsureWritable();\n        return currentClient.ExecuteResourceCommandAsync(resourceName, resourceType, command, options, cancellationToken);\n    }\n\n    public Task<string> UploadFileAsync(Stream fileStream, string fileName, long expectedSize, int interactionId, string inputName, CancellationToken cancellationToken)\n    {\n        EnsureWritable();\n        return currentClient.UploadFileAsync(fileStream, fileName, expectedSize, interactionId, inputName, cancellationToken);\n    }\n\n    private void EnsureWritable()\n    {\n        if (IsReadOnly)\n        {\n            throw new InvalidOperationException(\"Historical dashboard data is read-only.\");\n        }\n    }\n\n    private static async IAsyncEnumerable<WatchInteractionsResponseUpdate> EmptyInteractionsAsync()\n    {\n        await Task.CompletedTask.ConfigureAwait(false);\n        yield break;\n    }\n\n    public ValueTask DisposeAsync() => ValueTask.CompletedTask;\n}","sourceCodeStart":59,"sourceCodeEnd":88,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/ServiceClient/SelectedDashboardClient.cs#L59-L88","documentation":"SelectedDashboardClient wraps the live dashboard backend; when the session is in read-only (historical) mode, mutating operations must not run. EnsureWritable is called by mutating APIs (ClearConsoleLogsAsync, SendInteractionRequestAsync, ExecuteResourceCommandAsync, UploadFileAsync) and throws this InvalidOperationException when IsReadOnly is true. Historical data views are intentionally immutable.","triggerScenarios":"Calling ExecuteResourceCommandAsync, ClearConsoleLogsAsync, SendInteractionRequestAsync, or UploadFileAsync on a SelectedDashboardClient whose session targets historical/archived data (IsReadOnly == true).","commonSituations":"Dashboard tooling or extensions operating against a historical data view (e.g. viewing archived logs from a previous session) and invoking commands or uploads that only make sense on a live session.","solutions":["Check IsReadOnly before issuing any mutating call and no-op or prompt the user instead","Reconnect the dashboard client to a live session to perform commands or uploads","Restrict mutation entry points in UI so they are disabled in historical mode"],"exampleFix":"// before\nawait client.ExecuteResourceCommandAsync(command);\n// after\nif (!client.IsReadOnly)\n{\n    await client.ExecuteResourceCommandAsync(command);\n}","handlingStrategy":"validation","validationCode":"if (client.IsReadOnly)\n{\n    return; // historical sessions are immutable\n}","typeGuard":null,"tryCatchPattern":"try { await client.ExecuteResourceCommandAsync(cmd); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"read-only\"))\n{\n    logger.LogWarning(\"Cannot mutate historical dashboard data\");\n}","preventionTips":["Gate all mutating calls behind an IsReadOnly check","Disable command/upload UI affordances in historical mode","Reconnect to a live session to run commands"],"tags":["dashboard","read-only","historical-data"],"backgroundTag":"unsupported-operation","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}