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

  1. Read the Error description line to find the real failure (inner JsonException) and the Content description to see which event was involved
  2. Match the deserialization target type to the event's actual content type before calling DeserializeEventContent
  3. Update the emitting instrumentation or dashboard so both use the same GenAI content schema
  4. 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

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


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,
            _ => null

View on GitHub (pinned to 25830f84bd)