microsoft/aspire · error · InvalidDataException

' ' must be a JSON string.

Error message

'{CurrentStateProperty}' must be a JSON string.

What it means

The 'currentState' property of the migration state must be a JSON string (the state is embedded serialized). If the property holds a non-string JSON type such as an object, array, or number directly, InvalidDataException is thrown.

Solutions

  1. Re-encode currentState as a JSON string (escape the embedded object) or remove the property.
  2. Delete the file and let the next deployment regenerate it with the correct encoding.
  3. If you scripted state-file edits, serialize the current state with JsonNode/JsonSerializer to a string before writing.

Example fix

// before (state file)
{ "currentState": { "resources": {} } }
// after
{ "currentState": "{\"resources\":{}}" }
Defensive patterns

Strategy: validation

Validate before calling

// currentState must be a JSON string, not a raw object/array
if (migrationState["currentState"] is not (null or JsonValue v) ||
    (v is not null && !v.TryGetValue<string>(out _)))
{
    throw new InvalidDataException("currentState must be a JSON string.");
}

Type guard

static bool HasValidCurrentStateType(JsonObject migrationState) =>
    migrationState["currentState"] is null or JsonValue v && v.TryGetValue<string>(out _);

Try / catch

try { await manager.LoadStateAsync(ct); }
catch (InvalidDataException ex) when (ex.Message.Contains("currentState") && ex.Message.Contains("JSON string"))
{
    File.Move(statePath, statePath + ".corrupt", overwrite: true);
}

Prevention

When it happens

Trigger: Loading a migration state file where currentState was written as a raw JSON object instead of a JSON-escaped string — e.g. by a tool or hand edit that unescaped it.

Common situations: Hand-formatting the state file in an editor that unescaped the embedded JSON; an external script rewriting the file without preserving string encoding; schema drift between versions.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/57b4c421a87ec86f. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting/Pipelines/Internal/FileDeploymentStateManager.cs:766

        var legacyFallbackDisabled = migrationState[LegacyFallbackDisabledProperty]?.GetValue<bool>() ?? false;
        var legacyStateSnapshot = migrationState[LegacyStateProperty] switch
        {
            null => [],
            JsonObject legacyState => legacyState,
            _ => throw new InvalidDataException($"'{LegacyStateProperty}' must be a JSON object.")
        };
        var claimedSectionNames = migrationState[ClaimedSectionsProperty] switch
        {
            null => [],
            JsonValue claimedSectionsValue => ParseClaimedSectionNames(claimedSectionsValue),
            _ => throw new InvalidDataException($"'{ClaimedSectionsProperty}' must be a JSON string.")
        };
        var currentState = migrationState[CurrentStateProperty] switch
        {
            null => null,
            JsonValue stateValue => JsonNode.Parse(stateValue.GetValue<string>())?.AsObject()
                ?? throw new InvalidDataException($"'{CurrentStateProperty}' must contain a JSON object."),
            _ => throw new InvalidDataException($"'{CurrentStateProperty}' must be a JSON string.")
        };

        return new(legacyFallbackDisabled, legacyStateSnapshot, claimedSectionNames, currentState);
    }

    private static string[] ParseClaimedSectionNames(JsonValue claimedSectionsValue)
    {
        var claimedSectionNames = JsonSerializer.Deserialize<string?[]>(
            claimedSectionsValue.GetValue<string>());
        if (claimedSectionNames is null ||
            claimedSectionNames.Any(string.IsNullOrWhiteSpace))
        {
            throw new InvalidDataException(
                $"'{ClaimedSectionsProperty}' must contain a JSON array of non-empty strings.");
        }

        return [.. claimedSectionNames.Select(static sectionName => sectionName!)];
    }

View on GitHub (pinned to 25830f84bd)