{"record":{"id":"4b4ca6b4a8db0fa6","repo":"microsoft/aspire","slug":"deployment-state-must-contain-a-json-object","errorCode":null,"errorMessage":"Deployment state must contain a JSON object.","messagePattern":"Deployment state must contain a JSON object\\.","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Pipelines/Internal/DeploymentStateManagerBase.cs","lineNumber":138,"sourceCode":"        return ParseStateFile(fileContent);\n    }\n\n    private static JsonObject ParseStateFile(string? fileContent)\n    {\n        if (fileContent is null)\n        {\n            return [];\n        }\n\n        var jsonDocumentOptions = new JsonDocumentOptions\n        {\n            CommentHandling = JsonCommentHandling.Skip,\n            AllowTrailingCommas = true,\n        };\n\n        if (JsonNode.Parse(fileContent, documentOptions: jsonDocumentOptions) is not JsonObject flattenedState)\n        {\n            throw new InvalidDataException(\"Deployment state must contain a JSON object.\");\n        }\n\n        return JsonFlattener.UnflattenJsonObject(flattenedState);\n    }\n\n    /// <inheritdoc/>\n    public async Task SaveStateAsync(JsonObject state, CancellationToken cancellationToken = default)\n    {\n        await _stateLock.WaitAsync(cancellationToken).ConfigureAwait(false);\n        try\n        {\n            await SaveStateToStorageAsync(state, sectionName: null, originalSectionData: null, cancellationToken).ConfigureAwait(false);\n        }\n        finally\n        {\n            _stateLock.Release();\n        }\n    }","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Pipelines/Internal/DeploymentStateManagerBase.cs#L120-L156","documentation":"ParseStateFile parses the deployment state file and throws InvalidDataException if JsonNode.Parse does not yield a JsonObject, i.e. the file contains a JSON array, string, number, or invalid/truncated JSON rather than the expected top-level object. Parsing tolerates comments and trailing commas but the root must be an object.","triggerScenarios":"Calling LoadStateFileAsync/LoadStateFile when the deployment state file was hand-edited to an array or scalar, truncated by a crashed writer, or is empty/corrupt on disk.","commonSituations":"Manual edits of ~/.aspire/deployment state files; a previous run was killed mid-write; file synced/truncated by tooling; user pasted a JSON array of key/value pairs instead of an object.","solutions":["Open the state file and fix the root to be a JSON object ({...}); remove arrays or bare values at the top level.","If the file is truncated or corrupt, delete it and re-run the deployment so state is regenerated.","Restore the file from backup or source control if it was hand-edited."],"exampleFix":"// before (state file)\n[ { \"a\": 1 } ]\n// after\n{ \"a\": 1 }","handlingStrategy":"validation","validationCode":"// Validate the state file before use\nusing var doc = JsonNode.Parse(File.ReadAllText(statePath),\n    documentOptions: new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true });\nif (doc is not JsonObject)\n{\n    throw new InvalidDataException($\"{statePath} must contain a top-level JSON object.\");\n}","typeGuard":"static bool IsValidStateFile(string content) =>\n    JsonNode.Parse(content) is JsonObject;","tryCatchPattern":"try { var state = await manager.LoadStateFileAsync(path, ct); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"must contain a JSON object\"))\n{\n    // quarantine the corrupt file and start fresh\n    File.Move(path, path + \".corrupt\", overwrite: true);\n}","preventionTips":["Don't hand-edit deployment state files; if you must, keep the root as a JSON object.","Keep backups of the state file before manual changes.","Avoid killing processes mid-write; check for truncated files after crashes."],"tags":["json","state-file","validation"],"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"}