microsoft/autogen · error · Exception

Failed to deserialize response

Error message

Failed to deserialize response

What it means

Generic Exception("Failed to deserialize response") from MistralClient.CreateChatCompletionsAsync: JsonSerializer.DeserializeAsync<ChatCompletionResponse> returned null after the HTTP call succeeded (EnsureSuccessStatusCode passed). In practice this means the body was literally "null" or the JSON did not bind to any recognized shape mapped by the model, so the SDK cannot continue.

Source

Thrown at dotnet/src/AutoGen.Mistral/MistralClient.cs:43

        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
        this.baseUrl = baseUrl ?? this.baseUrl;
    }

    public MistralClient(HttpClient httpClient, string? baseUrl = null)
    {
        _httpClient = httpClient;
        _httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        this.baseUrl = baseUrl ?? this.baseUrl;
    }

    public async Task<ChatCompletionResponse> CreateChatCompletionsAsync(ChatCompletionRequest chatCompletionRequest)
    {
        chatCompletionRequest.Stream = false;
        var response = await HttpRequestRaw(HttpMethod.Post, chatCompletionRequest);
        response.EnsureSuccessStatusCode();

        var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<ChatCompletionResponse>(responseStream) ?? throw new Exception("Failed to deserialize response");
    }

    public async IAsyncEnumerable<ChatCompletionResponse> StreamingChatCompletionsAsync(ChatCompletionRequest chatCompletionRequest)
    {
        chatCompletionRequest.Stream = true;
        var response = await HttpRequestRaw(HttpMethod.Post, chatCompletionRequest, streaming: true);
        using var stream = await response.Content.ReadAsStreamAsync();
        using StreamReader reader = new StreamReader(stream);
        string? line = null;

        SseEvent currentEvent = new SseEvent();
        while ((line = await reader.ReadLineAsync()) != null)
        {
            if (!string.IsNullOrEmpty(line))
            {
                currentEvent.Data = line.Substring("data:".Length).Trim();
            }
            else // an empty line indicates the end of an event

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Capture the raw response body string (log it in a debug wrapper) to see what was actually returned.
  2. Verify the JsonSerializerOptions used match Mistral's snake_case/camelCase payload; fix PropertyNamingPolicy on the models.
  3. If a proxy returns null bodies with 200, fix or bypass the proxy.
  4. Upgrade AutoGen.Mistral / re-vendor the Mistral client models to the current API.

Example fix

// before
return await JsonSerializer.DeserializeAsync<ChatCompletionResponse>(responseStream) ?? throw new Exception("Failed to deserialize response");

// after
var json = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<ChatCompletionResponse>(json, JsonOptions)
       ?? throw new JsonException($"Unexpected payload: {json}"); // include body for diagnosis
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

static bool IsParseableCompletion(string json)
{
    using var doc = JsonDocument.Parse(json);
    return doc.RootElement.ValueKind == JsonValueKind.Object
        && (doc.RootElement.TryGetProperty("choices", out _) || doc.RootElement.TryGetProperty("error", out _));
}

Try / catch

catch (Exception ex) when (ex.Message == "Failed to deserialize response")
{
    logger.LogError(ex, "Mistral returned an unparseable body; capture raw payload via a logging HttpClient handler");
    throw;
}

Prevention

When it happens

Trigger: POST to Mistral /chat/completions (non-streaming) whose 200-status body is JSON null, an empty-ish object the deserializer maps to null, or a payload whose casing/naming policy differs from the model's JsonSerializerOptions so all properties are skipped.

Common situations: Mistral API change altering response field casing (camelCase vs snake_case) so nothing binds; intermediary proxies returning a null JSON literal with 200; mismatched vendored SDK models after an API update.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/a778b3f324714db1. Report an issue: GitHub.