{"record":{"id":"af21cb4bc0a9046e","repo":"microsoft/aspire","slug":"dashboard-run-metadata-for-run-runid-is-invalid","errorCode":null,"errorMessage":"Dashboard run metadata for '{run.RunId}' is invalid.","messagePattern":"Dashboard run metadata for '(.+?)' is invalid\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/ServiceClient/DashboardRunStore.cs","lineNumber":307,"sourceCode":"        {\n            // The current run has the store's lifetime lock, and a selected historical run has a lease.\n            // Only an unselected historical run needs a temporary lock while its metadata is updated.\n            using var runLock = storedRun.IsCurrent || storedRun.IsLeased\n                ? null\n                : TryOpenRunLock(runDirectory)\n                    ?? throw new InvalidOperationException($\"Dashboard run '{storedRun.RunId}' is no longer available.\");\n            UpdatePinnedState(storedRun, runDirectory, isPinned);\n        }\n    }\n\n    private void UpdatePinnedState(DashboardRunDescriptor run, string runDirectory, bool isPinned)\n    {\n        var metadataPath = Path.Combine(runDirectory, \"run.json\");\n        var metadata = JsonSerializer.Deserialize<DashboardRunMetadata>(File.ReadAllText(metadataPath));\n        if (metadata?.SchemaVersion != run.SchemaVersion ||\n            !string.Equals(metadata.RunId, run.RunId, StringComparison.Ordinal))\n        {\n            throw new InvalidDataException($\"Dashboard run metadata for '{run.RunId}' is invalid.\");\n        }\n\n        var updatedMetadata = metadata with { IsPinned = isPinned };\n        WriteMetadata(updatedMetadata, metadataPath);\n        if (string.Equals(run.RunId, RunId, StringComparison.Ordinal))\n        {\n            _metadata = updatedMetadata;\n        }\n\n        run.IsPinned = isPinned;\n    }\n\n    public void PublishRun()\n    {\n        if (_metadataPath is null || _metadataPublished)\n        {\n            return;\n        }","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/ServiceClient/DashboardRunStore.cs#L289-L325","documentation":"UpdatePinnedState reads the run's run.json metadata and validates it before rewriting the pinned flag. It throws InvalidDataException when the deserialized metadata is null, its SchemaVersion differs from the in-memory run descriptor's, or its RunId doesn't match — i.e. the on-disk metadata doesn't correspond to the run being updated.","triggerScenarios":"Pinning a run whose run.json is missing, corrupt JSON, hand-edited, or was written by a different dashboard version (SchemaVersion mismatch) or belongs to a different RunId.","commonSituations":"Manually editing or copying run.json between run directories; a partially-written/corrupted metadata file after a crash; upgrading the dashboard so the persisted SchemaVersion no longer matches; deleting/renaming run directories by hand.","solutions":["Restore or regenerate the run.json file so its SchemaVersion and RunId match the run being pinned","Remove the corrupted historical run directory and let the dashboard recreate consistent state","Do not hand-edit run metadata; change pinned state through the dashboard or SetRunPinned API"],"exampleFix":"// before: hand-edited run.json breaks the pin operation\n// after: verify metadata before pinning\nvar meta = JsonSerializer.Deserialize<DashboardRunMetadata>(File.ReadAllText(Path.Combine(runDir, \"run.json\")));\nif (meta is null || meta.RunId != run.RunId || meta.SchemaVersion != run.SchemaVersion)\n{\n    throw new InvalidDataException($\"Corrupt run metadata for '{run.RunId}'; delete or restore the run directory.\");\n}","handlingStrategy":"validation","validationCode":"var metaPath = Path.Combine(runDirectory, \"run.json\");\nif (!File.Exists(metaPath)) return false;\ntry\n{\n    var meta = JsonSerializer.Deserialize<DashboardRunMetadata>(File.ReadAllText(metaPath));\n    return meta is not null && meta.RunId == run.RunId && meta.SchemaVersion == run.SchemaVersion;\n}\ncatch (JsonException)\n{\n    return false;\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    store.SetRunPinned(run, isPinned: true);\n}\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"is invalid\"))\n{\n    // quarantine/delete the corrupted run directory\n}","preventionTips":["Never hand-edit run.json or copy it between run directories","Recreate run directories after crashes that may leave partial metadata","After dashboard upgrades, verify persisted metadata SchemaVersion before mutating runs"],"tags":["dashboard","json","metadata-validation","persistence"],"backgroundTag":"json-parse-error","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}