Skip to content

.NET: [Bug]: Frontend tools break persisted chat history #6479

@kpobb1989

Description

@kpobb1989

Description

When using AG-UI with Microsoft Agent Framework and a persisted chat history provider such as Cosmos DB, frontend-only tools can make a conversation unreplayable after the first tool interaction.
The frontend sends available tools to the backend on each request. In this flow, the agent may emit a  functionCall  for a tool that is meant to be rendered by the frontend, such as a map tool. However, the frontend tool does not naturally produce  functionResult , because the result is not generated by the server. The UI simply renders the tool action.

It is not obvious that the frontend must always send a matching  functionResult  back to the backend. Even if the frontend attempts to do so, there is no guarantee that the result will reach the backend reliably because of network interruption, page refresh, tab closure, or other client-side failures.
As a result, the persisted conversation history can contain a  functionCall  without a matching  functionResult . On the next request, when the backend reloads the conversation from Cosmos DB, AI behind Agent Framework rejects to process the conversation history because it is incomplete.

Expected behavior
Frontend-only AG-UI tools should not make persisted chat history invalid or unreplayable.
A conversation should remain resumable after refresh or reconnect, even if the frontend-rendered tool result was never sent back to the backend.

Actual behavior
When a frontend tool is used, the agent stores a  functionCall  in the conversation history. Since the frontend does not send a matching  functionResult , the persisted history becomes inconsistent. On the next request, the backend reloads invalid history and the conversation can no longer continue.

Why this is a problem
This design assumes the frontend can always send a matching  functionResult , but that is not a safe assumption.
Frontend-only tool execution is inherently unreliable from a transport perspective. The result may never make it back to the server due to:
• network failure.
• page refresh.
• browser tab closure.
• client crashes.
• other transient frontend issues.
Because of this, persisting a  functionCall  that depends on a frontend-returned  functionResult  makes the conversation fragile and difficult to resume.

Reproduction steps

  1. Create a .NET app using Microsoft Agent Framework with AG-UI enabled.
  2. Configure a persisted chat history provider such as Cosmos DB.
  3. Expose a frontend-only tool such as  render_map .
  4. Send a user message that causes the agent to call that tool.
  5. Let the frontend render the tool output without sending a backend  functionResult .
  6. Refresh the page or send another message in the same thread.
  7. Observe that the conversation history can no longer be replayed correctly.

Notes
AG-UI frontend tools are not the same as backend-executed tools. The backend should not assume a frontend-rendered tool will always return a durable  functionResult .

Code Sample

Backend

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.CosmosNoSql;
using Microsoft.Agents.AI.OpenAI;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAGUI();

var app = builder.Build();

var openAiClient = new AzureOpenAIClient(
    new Uri(builder.Configuration["AZURE_OPENAI_ENDPOINT"]!),
    new DefaultAzureCredential());

var chatClient = openAiClient.GetChatClient(builder.Configuration["AZURE_OPENAI_DEPLOYMENT"]!);

var cosmosProvider = new CosmosChatHistoryProvider(
    endpoint: builder.Configuration["COSMOS_ENDPOINT"]!,
    databaseName: builder.Configuration["COSMOS_DATABASE"]!,
    containerName: builder.Configuration["COSMOS_CONTAINER"]!,
    credential: new DefaultAzureCredential());

var agent = chatClient.CreateAIAgent(new ChatClientAgentOptions
{
    Name = "agui-repro",
    Instructions = """
    You are a helpful assistant.
    If the user asks to show a location on a map, use the render_map tool.
    """,
    ChatHistoryProvider = cosmosProvider
});

app.MapAGUI("/agent", agent);

await app.RunAsync();

Frontend

const renderMapTool = {
  name: "render_map",
  description: "Render markers on a map in the UI",
  parameters: {
    type: "object",
    properties: {
      latitude: { type: "number" },
      longitude: { type: "number" },
      label: { type: "string" }
    },
    required: ["latitude", "longitude", "label"]
  }
};

await fetch("/agent", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    threadId: "demo-thread-1",
    messages: [
      { role: "user", content: "Show Tokyo on a map" }
    ],
    tools: [renderMapTool]
  })
});

Error Messages / Stack Traces

HTTP 400 (invalid, request_ error. ) Parameter: input No tool output found for function call call WglIXOb6N37wXIYy/MO7UyyP.

Package Versions

Microsoft.Agents.AI.Hosting.AGUI.AspNetCore 1.9.0-preview.260603.1

.NET Version

.NET 10

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Bug.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions