Skip to content

.NET: [Bug]: ChatClientAgent throws when using ChatHistoryProvider with Azure OpenAI Responses API and store=false #4937

@luismanez

Description

@luismanez

Description

When using Microsoft.Agents.AI with Azure OpenAI Responses API through AsIChatClient(), ChatClientAgent throws if a ChatHistoryProvider is configured, even when the request is explicitly stateless (store=false / StoredOutputEnabled = false).

The exception is:

Only ConversationId or ChatHistoryProvider may be used, but not both. The service returned a conversation id indicating server-side chat history management, but the agent has a ChatHistoryProvider configured.

From captured HTTP traffic:

  • the request is sent with "store": false
  • the final JSON response does not contain a conversation field
  • the final JSON response has "previous_response_id": null

This suggests the issue is not caused by Azure OpenAI returning a real conversation id. Instead, it appears that somewhere in the adapter chain, a non-null ConversationId is surfaced to ChatClientAgent, causing MAF to incorrectly assume server-side history management.

From source inspection, ChatClientAgent throws whenever the adapted ChatResponse / ChatResponseUpdate contains a non-null ConversationId, regardless of whether the underlying provider is actually using a real server-side conversation.

Relevant MAF code:

  • RunCoreStreamingAsync(...) combines updates via ToChatResponse() and then calls UpdateSessionConversationId(...)
  • RunCoreAsync(...) also calls UpdateSessionConversationId(...)
  • UpdateSessionConversationId(...) throws if both ConversationId and ChatHistoryProvider are present

This is especially problematic for Azure OpenAI Responses scenarios where the caller explicitly wants to manage history client-side with ChatHistoryProvider.

Code Sample

using Azure;
using Azure.AI.OpenAI;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI.Responses;

string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
string apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")!;
string deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT")!;

var azureClient = new AzureOpenAIClient(
    new Uri(endpoint),
    new AzureKeyCredential(apiKey));

var responsesClient = azureClient.GetResponsesClient(deployment);

IChatClient chatClient = responsesClient.AsIChatClient();

var agent = new ChatClientAgent(
    chatClient,
    new ChatClientAgentOptions
    {
        Name = "ReproAgent",
        ChatOptions = new ChatOptions
        {
            Instructions = "You are a helpful assistant.",
            RawRepresentationFactory = _ => new CreateResponseOptions
            {
                StoredOutputEnabled = false
            }
        },
        ChatHistoryProvider = new InMemoryChatHistoryProvider()
    });

AgentSession session = await agent.CreateSessionAsync();

// Non-streaming
var response = await agent.RunAsync("Say hello.", session);

// Streaming
await foreach (var update in agent.RunStreamingAsync("Say hello.", session))
{
    Console.Write(update.Text);
}


## Captured HTTP Request


{
  "model": "gpt-4.1-mini",
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "How would you define Zava business in just 5 words?"
        }
      ]
    }
  ],
  "stream": true,
  "store": false
}


## Captured HTTP Response


{
  "id": "resp_0c09e9748364cb3c0169c573ecf76881909672744ee7074f94",
  "object": "response",
  "status": "completed",
  "model": "gpt-4.1-mini",
  "output": [
    {
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "..."
        }
      ]
    }
  ],
  "previous_response_id": null,
  "store": false
}


Notably, there is no `conversation` field in the response.

Error Messages / Stack Traces

Only ConversationId or ChatHistoryProvider may be used, but not both. The service returned a conversation id indicating server-side chat history management, but the agent has a ChatHistoryProvider configured.

   at Microsoft.Agents.AI.ChatClientAgent.UpdateSessionConversationId(ChatClientAgentSession session, String responseConversationId, CancellationToken cancellationToken)
   at Microsoft.Agents.AI.ChatClientAgent.<RunCoreStreamingAsync>d__29.MoveNext()
   at Microsoft.Agents.AI.ChatClientAgent.<RunCoreStreamingAsync>d__29.System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(Int16 token)
   at Microsoft.Agents.AI.AIAgent.<RunStreamingAsync>d__33.MoveNext()
   at Microsoft.Agents.AI.AIAgent.<RunStreamingAsync>d__33.MoveNext()
   at Microsoft.Agents.AI.AIAgent.<RunStreamingAsync>d__33.System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(Int16 token)

Package Versions

Microsoft.Agents.AI 1.0.0-rc3, Microsoft.Extensions.AI 10.3.0, Microsoft.Agents.AI.OpenAI 1.0.0-rc3

.NET Version

.NET 10

Additional Context

A few observations that may help narrow this down:

  1. This reproduces with Azure OpenAI Responses API.
  2. The outgoing request explicitly includes "store": false.
  3. The final HTTP response does not contain a conversation field.
  4. The final HTTP response has "previous_response_id": null.
  5. MAF throws only because ChatClientAgent receives a non-null ConversationId from the adapted ChatResponse / ChatResponseUpdate.
  6. This appears to happen even though the underlying HTTP response does not contain a real server-managed conversation id.

Potentially relevant MAF source:

  • dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
  • dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgentTests.cs

Behavioral expectation:

  • If ChatHistoryProvider is configured and the request is stateless (store=false), ChatClientAgent should not treat the returned value as a real server-side conversation id and should not throw.

Actual behavior:

  • ChatClientAgent throws as if the provider were managing server-side history, even though the Responses request/response indicates a stateless flow.

Note

I am aware that the underlying adapter stack may be mapping Responses semantics into ConversationId in a way that is broader than a true server-managed conversation. Even so, from the MAF side this currently makes ChatHistoryProvider unusable for this scenario, despite the request being explicitly stateless.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions