microsoft/aspire · error · JsonException

Expected a JSON array.

Error message

Expected a JSON array.

What it means

DeserializeArrayIncrementally parses a JSON string as an array of T using Utf8JsonReader. If the first token is not the start of an array, a JsonException with this message is thrown; truly malformed JSON exceptions are intentionally allowed to propagate. This guards the incremental reader so it never loops over a non-array document.

Solutions

  1. Wrap the payload in an array before parsing: if the first non-whitespace char is '{', serialize as [payload].
  2. For backwards compatibility, detect a leading '{' and fall back to parsing a single object then treat it as a one-element list.
  3. Validate the stored JSON shape where it is written to prevent bad payloads from being persisted.

Example fix

// before
var messages = GenAIMessageParsingHelper.DeserializeArrayIncrementally<ChatMessage>(json);
// after
var trimmed = json.TrimStart();
if (trimmed.StartsWith('{'))
{
    json = "[" + json + "]"; // older payloads stored a single message object
}
var messages = GenAIMessageParsingHelper.DeserializeArrayIncrementally<ChatMessage>(json);
Defensive patterns

Strategy: try-catch

Validate before calling

var trimmed = json.TrimStart();
if (trimmed.Length == 0 || (trimmed[0] != '['))
{
    // normalize single-object payloads to arrays before parsing
    if (trimmed.StartsWith('{')) json = "[" + json + "]";
}

Try / catch

try
{
    items = GenAIMessageParsingHelper.DeserializeArrayIncrementally<T>(json);
}
catch (JsonException ex)
{
    logger.LogWarning(ex, "Payload is not a JSON array; skipping malformed telemetry entry");
}

Prevention

When it happens

Trigger: Calling DeserializeArrayIncrementally with input whose first JSON token is an object, string, number, or null instead of '[' — e.g. telemetry/store payloads where a single GenAI message object was saved without wrapping it in an array.

Common situations: Older OpenTelemetry GenAI telemetry stored a single chat message object rather than an array of messages; hand-edited or truncated JSON files in the telemetry store; producers changed shape between versions.

Related errors


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

Appendix: source

Thrown at src/Aspire.Dashboard/Model/GenAI/GenAIMessageParsingHelper.cs:36

    {
        var bytes = Encoding.UTF8.GetBytes(json);
        var readerOptions = new JsonReaderOptions
        {
            CommentHandling = JsonCommentHandling.Skip,
            AllowTrailingCommas = true,
        };
        var reader = new Utf8JsonReader(bytes, readerOptions);
        return DeserializeArrayIncrementally(ref reader, readElement);
    }

    internal static (List<T> items, bool truncated) DeserializeArrayIncrementally<T>(ref Utf8JsonReader reader, ReadElement<T> readElement)
    {
        var items = new List<T>();

        // Read start of array. Let exceptions propagate for truly invalid JSON.
        if (!reader.Read() || reader.TokenType != JsonTokenType.StartArray)
        {
            throw new JsonException("Expected a JSON array.");
        }

        while (true)
        {
            bool readSuccess;
            try
            {
                readSuccess = reader.Read();
            }
            catch (JsonException)
            {
                return (items, true);
            }

            if (!readSuccess)
            {
                return (items, true);
            }

View on GitHub (pinned to 25830f84bd)