microsoft/aspire · error · JsonException
Expected property name.
Error message
Expected property name.
What it means
GenAIMessageParsingHelper.ReadChatMessage manually walks a GenAI message JSON object with Utf8JsonReader. After reading '{' or consuming a property value it expects the next token to be a property name; anything else means the JSON is not shaped like a chat message object, so it throws JsonException. It is a guard against malformed or non-object payloads being rendered in the GenAI visualizer.
Solutions
- Inspect the raw JSON stored in the telemetry entry and confirm each chat message is a JSON object like {"role":"user","parts":[...]}
- Upgrade the emitting instrumentation (e.g. OpenTelemetry .NET GenAI exporters) so messages follow the documented GenAI content schema
- Check for truncation of the recorded body (size limits when exporting) and re-export with larger limits
- Wrap parsing in DeserializeWithErrorHandling so a malformed message is surfaced as a description instead of a raw JsonException
Example fix
// before
var message = JsonDocument.Parse(rawBody).RootElement[0]; // an array, not an object
// after
if (message.ValueKind == JsonValueKind.Object && message.TryGetProperty("role", out _))
{
var (role, parts, truncated) = GenAIMessageParsingHelper.ReadChatMessage(message);
} Defensive patterns
Strategy: try-catch
Validate before calling
using var doc = JsonDocument.Parse(json);
var ok = doc.RootElement.ValueKind == JsonValueKind.Object &&
doc.RootElement.TryGetProperty("role", out _);
if (!ok) throw new FormatException("Chat message is not a JSON object with a role property."); Type guard
static bool IsChatMessageObject(JsonElement e) =>
e.ValueKind == JsonValueKind.Object && e.TryGetProperty("role", out _); Try / catch
try
{
var (role, parts, truncated) = GenAIMessageParsingHelper.ReadChatMessage(element);
}
catch (JsonException ex)
{
logger.LogWarning(ex, "Malformed GenAI chat message; rendering raw content instead.");
} Prevention
- Validate telemetry bodies with JsonDocument.Parse before incremental Utf8JsonReader parsing
- Pin instrumentation versions so emitters and the dashboard agree on the message schema
- Avoid hand-editing exported telemetry JSON
When it happens
Trigger: Reading a telemetry GenAI chat message whose JSON at the current position is not an object with property names — e.g. the value of a message field is an array, string, or number instead of an object, or the JSON is truncated mid-structure so a non-property token appears where a name is expected.
Common situations: OTLP logs/traces where the GenAI message body was serialized by a different schema version or custom instrumentation; a user pasting/importing hand-edited telemetry JSON; truncated payloads saved from an export.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Expected a JSON array.
- Expected start of chat message object.
- Unexpected end of JSON while reading property value.
- Unexpected end of JSON while reading role value.
- Unexpected end of JSON while skipping property value.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/5818d6b5ad86fda6.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Dashboard/Model/GenAI/GenAIMessageParsingHelper.cs:107
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("Expected start of chat message object.");
}
string? role = null;
List<MessagePart>? parts = null;
var partsTruncated = false;
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException("Expected property name.");
}
var propertyName = reader.GetString();
switch (propertyName)
{
case "role":
if (!reader.Read())
{
throw new JsonException("Unexpected end of JSON while reading role value.");
}
role = reader.GetString();
break;
case "parts":
// DeserializeArrayIncrementally reads the StartArray token itself.
(parts, partsTruncated) = DeserializeArrayIncrementally<MessagePart>(ref reader, ReadMessagePart);
break;
default:View on GitHub (pinned to 25830f84bd)