microsoft/aspire · error · InvalidOperationException
Error deserializing GenAI message content. Error description
Error message
Error deserializing GenAI message content.
Error description: {ex.GetType().FullName}: {ex.Message}
Content description: {description} What it means
DeserializeWithErrorHandling wraps System.Text.Json deserialization of GenAI event content for the visualizer. When deserialization throws, it rethrows InvalidOperationException with the exception type, message, and a content description — deliberately excluding the raw JSON because it may contain sensitive user data. The inner exception retains the root cause.
Solutions
- Read the Error description line to find the real failure (inner JsonException) and the Content description to see which event was involved
- Match the deserialization target type to the event's actual content type before calling DeserializeEventContent
- Update the emitting instrumentation or dashboard so both use the same GenAI content schema
- Validate/normalize the JSON shape at ingest time if you control the importer
Example fix
// before
var content = JsonSerializer.Deserialize<GenAIEventContent>(json, jsonTypeInfo);
// after
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("type", out var t) &&
t.GetString() == "text")
{
var content = JsonSerializer.Deserialize<TextContent>(json, textJsonTypeInfo);
} Defensive patterns
Strategy: try-catch
Validate before calling
try { using var _ = JsonDocument.Parse(json); }
catch (JsonException) { throw new FormatException($"Event content '{description}' is not valid JSON."); } Type guard
static bool LooksLikeJson(string? s) =>
!string.IsNullOrWhiteSpace(s) &&
(s.TrimStart().StartsWith('{') || s.TrimStart().StartsWith('[')); Try / catch
try
{
content = DeserializeEventContent<T>(json, jsonTypeInfo, description);
}
catch (InvalidOperationException ex)
{
logger.LogWarning(ex, "Failed to deserialize GenAI event content for {Description}.", description);
} Prevention
- Match the deserialization type to the event's declared content type
- Keep emitter SDK and dashboard schema versions aligned
- Never assume telemetry attribute strings are valid JSON of the expected shape
When it happens
Trigger: Calling DeserializeEventContent for GenAI telemetry event bodies whose JSON doesn't match the target type T (wrong union member, missing discriminator, mismatched JSON type), e.g. deserializing a function-call event body as a text content type.
Common situations: Mixed schema versions between the emitting SDK and the dashboard; telemetry attributes holding non-JSON or differently shaped strings; viewing traces produced by third-party GenAI instrumentation.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- Expected a JSON array.
- Expected start of chat message object.
- Cannot convert string
- Dashboard run metadata for
- Expected a non-empty Kubernetes MicroTime value.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/f5d196c566d31623.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Dashboard/Model/GenAI/GenAIVisualizerDialogViewModel.cs:573
}
var args = GenAIMessageParsingHelper.TryParseStringJsonNode(function.Arguments);
messagePartViewModels.Add(GenAIItemPartViewModel.CreateMessagePart(new ToolCallRequestPart { Name = function.Name, Arguments = args }));
}
}
}
}
private static TValue DeserializeWithErrorHandling<TValue>(string description, string json, JsonTypeInfo<TValue> jsonTypeInfo)
{
try
{
return JsonSerializer.Deserialize<TValue>(json, jsonTypeInfo)!;
}
catch (Exception ex)
{
// Don't include JSON in exception message because it could contain sensitive data.
throw new InvalidOperationException(
$"""
Error deserializing GenAI message content.
Error description: {ex.GetType().FullName}: {ex.Message}
Content description: {description}
""", ex);
}
}
private static bool TryMapEventName(string name, [NotNullWhen(true)] out GenAIItemType? type)
{
type = name switch
{
"gen_ai.system.message" => GenAIItemType.SystemMessage,
"gen_ai.user.message" => GenAIItemType.UserMessage,
"gen_ai.assistant.message" => GenAIItemType.AssistantMessage,
"gen_ai.tool.message" => GenAIItemType.ToolMessage,
"gen_ai.choice" => GenAIItemType.OutputMessage,
_ => nullView on GitHub (pinned to 25830f84bd)