{"record":{"id":"31f1c195c0ecfd9f","repo":"microsoft/aspire","slug":"legacystateproperty-must-be-a-json-object","errorCode":null,"errorMessage":"'{LegacyStateProperty}' must be a JSON object.","messagePattern":"'(.+?)' must be a JSON object\\.","errorType":"exception","errorClass":"InvalidDataException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting/Pipelines/Internal/FileDeploymentStateManager.cs","lineNumber":753,"sourceCode":"        return ParseMigrationState(migrationState);\n    }\n\n    private static MigrationState LoadMigrationStateFile(string canonicalStatePath)\n    {\n        var migrationStatePath = GetMigrationStatePath(canonicalStatePath);\n        return File.Exists(migrationStatePath)\n            ? ParseMigrationState(LoadStateFile(migrationStatePath))\n            : new(false, [], [], null);\n    }\n\n    private static MigrationState ParseMigrationState(JsonObject migrationState)\n    {\n        var legacyFallbackDisabled = migrationState[LegacyFallbackDisabledProperty]?.GetValue<bool>() ?? false;\n        var legacyStateSnapshot = migrationState[LegacyStateProperty] switch\n        {\n            null => [],\n            JsonObject legacyState => legacyState,\n            _ => throw new InvalidDataException($\"'{LegacyStateProperty}' must be a JSON object.\")\n        };\n        var claimedSectionNames = migrationState[ClaimedSectionsProperty] switch\n        {\n            null => [],\n            JsonValue claimedSectionsValue => ParseClaimedSectionNames(claimedSectionsValue),\n            _ => throw new InvalidDataException($\"'{ClaimedSectionsProperty}' must be a JSON string.\")\n        };\n        var currentState = migrationState[CurrentStateProperty] switch\n        {\n            null => null,\n            JsonValue stateValue => JsonNode.Parse(stateValue.GetValue<string>())?.AsObject()\n                ?? throw new InvalidDataException($\"'{CurrentStateProperty}' must contain a JSON object.\"),\n            _ => throw new InvalidDataException($\"'{CurrentStateProperty}' must be a JSON string.\")\n        };\n\n        return new(legacyFallbackDisabled, legacyStateSnapshot, claimedSectionNames, currentState);\n    }\n","sourceCodeStart":735,"sourceCodeEnd":771,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting/Pipelines/Internal/FileDeploymentStateManager.cs#L735-L771","documentation":"When reading the migration state record, the manager requires the 'legacyState' property to be either absent/null or a JsonObject; any other JSON type (array, string, number) throws InvalidDataException. This is part of validating the legacy-to-current state-file migration snapshot.","triggerScenarios":"Loading a migration/state file where the legacyState property holds a non-object JSON value — e.g. after a hand edit, a bad migration write, or corruption.","commonSituations":"Manual editing of the migration state file; an interrupted or buggy write of the legacy snapshot; version skew between Aspire versions that changed the file layout.","solutions":["Fix the legacyState property in the file to be a JSON object, or remove the property entirely (null is accepted).","Delete the corrupt state/migration file and let the next deployment recreate it.","Restore the file from a known-good backup."],"exampleFix":"// before (state file)\n{ \"legacyState\": \"not-an-object\" }\n// after\n{ \"legacyState\": { \"key\": \"value\" } }","handlingStrategy":"validation","validationCode":"// Validate before handing the file to the manager\nif (migrationState[\"legacyState\"] is not (null or JsonObject))\n{\n    throw new InvalidDataException(\"legacyState must be a JSON object or absent.\");\n}","typeGuard":"static bool HasValidLegacyState(JsonObject migrationState) =>\n    migrationState[\"legacyState\"] is null or JsonObject;","tryCatchPattern":"try { await manager.LoadStateAsync(ct); }\ncatch (InvalidDataException ex) when (ex.Message.Contains(\"legacyState\") && ex.Message.Contains(\"JSON object\"))\n{\n    // back up and regenerate the state file\n    File.Move(statePath, statePath + \".corrupt\", overwrite: true);\n}","preventionTips":["Don't hand-edit migration state files; let the pipeline own the schema.","If scripting edits, round-trip through System.Text.Json so property types stay correct.","Keep the whole file from one Aspire version — don't mix schemas."],"tags":["json","state-file","migration","type-mismatch"],"backgroundTag":"type-mismatch","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"}