{"record":{"id":"c18d87f75da1fe10","repo":"microsoft/aspire","slug":"expected-start-of-chat-message-object","errorCode":null,"errorMessage":"Expected start of chat message object.","messagePattern":"Expected start of chat message object\\.","errorType":"exception","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/Model/GenAI/GenAIMessageParsingHelper.cs","lineNumber":91,"sourceCode":"            catch (InvalidOperationException)\n            {\n                return (items, true);\n            }\n        }\n\n        return (items, false);\n    }\n\n    internal static MessagePart? ReadMessagePart(ref Utf8JsonReader reader)\n    {\n        return JsonSerializer.Deserialize(ref reader, GenAIMessagesContext.Default.MessagePart);\n    }\n\n    internal static (string role, List<MessagePart> parts, bool partsTruncated) ReadChatMessage(ref Utf8JsonReader reader)\n    {\n        if (reader.TokenType != JsonTokenType.StartObject)\n        {\n            throw new JsonException(\"Expected start of chat message object.\");\n        }\n\n        string? role = null;\n        List<MessagePart>? parts = null;\n        var partsTruncated = false;\n\n        while (reader.Read())\n        {\n            if (reader.TokenType == JsonTokenType.EndObject)\n            {\n                break;\n            }\n\n            if (reader.TokenType != JsonTokenType.PropertyName)\n            {\n                throw new JsonException(\"Expected property name.\");\n            }\n","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/Model/GenAI/GenAIMessageParsingHelper.cs#L73-L109","documentation":"ReadChatMessage parses one chat message from a Utf8JsonReader and requires the current token to be StartObject. If the reader is positioned at any other token (array, string, number), a JsonException with this message is thrown. Each element of a chat-message array must be a JSON object with role and parts fields.","triggerScenarios":"An element inside the messages array is not an object — e.g. the array contains plain strings, numbers, or nulls instead of {\"role\":..., \"parts\":[...]} objects.","commonSituations":"Telemetry producers emitting non-conformant message arrays (legacy or custom exporters); hand-crafted test fixtures; schema drift where messages were stored as bare strings.","solutions":["Fix the producer to emit chat messages as JSON objects with role and parts fields.","Add a pre-check on the reader token type and skip/coerce non-object elements before calling ReadChatMessage.","Validate stored payloads against the expected GenAI message schema at write time."],"exampleFix":"// before\nvar (role, parts, truncated) = GenAIMessageParsingHelper.ReadChatMessage(ref reader);\n// after\nif (reader.TokenType != JsonTokenType.StartObject)\n{\n    reader.Skip(); // tolerate malformed element\n    return null;\n}\nvar (role, parts, truncated) = GenAIMessageParsingHelper.ReadChatMessage(ref reader);","handlingStrategy":"try-catch","validationCode":"if (reader.TokenType != JsonTokenType.StartObject)\n{\n    reader.Skip(); // element is not a message object\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    var (role, parts, truncated) = GenAIMessageParsingHelper.ReadChatMessage(ref reader);\n}\ncatch (JsonException ex)\n{\n    logger.LogWarning(ex, \"Chat message element is not a JSON object; skipping\");\n}","preventionTips":["Ensure producers emit each chat message as an object with role and parts fields.","Validate messages arrays against the GenAI schema before storing.","Treat malformed array elements defensively (skip rather than abort the whole list)."],"tags":["json","parsing","genai","schema","dashboard"],"backgroundTag":"schema-validation-failed","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"}