microsoft/aspire · error · JsonException

Missing 'type' property.

Error message

Missing 'type' property.

What it means

GenAIMessages custom converter Read parses the whole message into a JsonDocument and requires a discriminator "type" property to pick the concrete MessagePart. If the property is absent the JSON does not conform to the GenAI message-part union schema, so JsonException("Missing 'type' property.") is thrown.

Solutions

  1. Add the "type" discriminator to every message part (e.g. "text", "functionCall", "functionResult", "media") before serializing
  2. Upgrade the emitting instrumentation to a version that writes the standard GenAI content schema
  3. Inspect the telemetry attribute containing the message body and fix its structure at the source
  4. If integrating a custom schema, pre-map your format to the expected {type, ...} shape before deserialization

Example fix

// before
{"text": "hello"}
// after
{"type": "text", "text": "hello"}
Defensive patterns

Strategy: validation

Validate before calling

using var doc = JsonDocument.Parse(json);
if (!doc.RootElement.TryGetProperty("type", out var t) || t.ValueKind != JsonValueKind.String)
    throw new FormatException("Message part requires a string 'type' discriminator.");

Type guard

static bool IsTypedMessagePart(JsonElement e) =>
    e.ValueKind == JsonValueKind.Object &&
    e.TryGetProperty("type", out var t) &&
    t.ValueKind == JsonValueKind.String;

Try / catch

try { part = JsonSerializer.Deserialize<MessagePart>(json, jsonTypeInfo); }
catch (JsonException ex) when (ex.Message.Contains("Missing 'type' property"))
{
    logger.LogWarning(ex, "Message part missing type discriminator.");
}

Prevention

When it happens

Trigger: Deserializing a message part object that lacks the discriminator, e.g. {"text":"hi"} instead of {"type":"text","text":"hi"}; feeding arbitrary JSON into GenAI message deserialization from telemetry attributes.

Common situations: Instrumentation emitting an older or custom GenAI content schema without the type field; hand-built payloads in tests or imports; vendor-specific extensions replacing the standard part shape.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Dashboard/Model/GenAI/GenAIMessages.cs:216

    public string? Name { get; set; }
    public string? Description { get; set; }
    internal ToolDefinitionSchema? Parameters { get; set; }
}

/// <summary>
/// Handles polymorphic serialization and deserialization of <see cref="MessagePart"/> types.
/// </summary>
internal sealed class MessagePartConverter : JsonConverter<MessagePart>
{
    public override MessagePart? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        using var doc = JsonDocument.ParseValue(ref reader);
        string? type = null;
        try
        {
            if (!doc.RootElement.TryGetProperty("type", out var typeProp))
            {
                throw new JsonException("Missing 'type' property.");
            }

            type = typeProp.GetString();

            return type switch
            {
                MessagePart.TextType => doc.RootElement.Deserialize<TextPart>(options),
                MessagePart.ToolCallType => TryParseStringArguments(doc.RootElement.Deserialize<ToolCallRequestPart>(options)),
                MessagePart.ToolCallResponseType => doc.RootElement.Deserialize<ToolCallResponsePart>(options),
                MessagePart.BlobType => doc.RootElement.Deserialize<BlobPart>(options),
                MessagePart.FileType => doc.RootElement.Deserialize<FilePart>(options),
                MessagePart.UriType => doc.RootElement.Deserialize<UriPart>(options),
                MessagePart.ReasoningType => doc.RootElement.Deserialize<ReasoningPart>(options),
                MessagePart.ServerToolCallType => TryParseServerToolCallArguments(doc.RootElement.Deserialize<ServerToolCallPart>(options)),
                MessagePart.ServerToolCallResponseType => doc.RootElement.Deserialize<ServerToolCallResponsePart>(options),
                _ => doc.RootElement.Deserialize<GenericPart>(options),
            };
        }

View on GitHub (pinned to 25830f84bd)