microsoft / microsoft/agent-framework

.NET: [Bug]: ChatClientAgent drops ResponseFormat before invoking underlying IChatClient

Open
#8,339 1 comment 0 reactions 1 assignee View on GitHub

@moonbox3 is already working on this.

Since Sep 13, 2026.

.NET likely-fixed
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

### Description

### Minimal reproduction

The issue can be reproduced without relying on any provider-specific behavior.

A decorating `IChatClient` captures the `ChatOptions` that ultimately reach the underlying chat client.

When `ResponseFormat` is supplied through `AgentRunOptions`:

```csharp
var options = new AgentRunOptions
{
ResponseFormat =
ChatResponseFormat.ForJsonSchema()
};

await agent.RunAsync(
"Return a structured response.",
options: options);
```

the captured `ChatOptions.ResponseFormat` is `null`.

However, when the same response format is supplied directly to `IChatClient.GetResponseAsync`:

```csharp
var options = new ChatOptions
{
ResponseFormat =
ChatResponseFormat.ForJsonSchema()
};

await chatClient.GetResponseAsync(messages, options);
```

`ResponseFormat` reaches the underlying client correctly.

I originally discovered this using the Anthropic provider. Calling the Anthropic `IChatClient` directly correctly generates Anthropic's `output_config.format`, while routing the same request through `ChatClientAgent` / `AIAgent.RunAsync` causes the response format to disappear before the provider is invoked.

I also reproduced it:

* with and without tools
* on the initial model call
* after a tool-call/result turn
* using `RunAsync()`
* using explicit `AgentRunOptions.ResponseFormat`
* with the response format configured at agent construction time

This suggests the problem is in `ChatClientAgent` response-format propagation rather than in the Anthropic adapter or tool-calling path.

### Expected

`AgentRunOptions.ResponseFormat` and the response format implied by `RunAsync()` should be propagated into the `ChatOptions.ResponseFormat` passed to the underlying `IChatClient`.

### Actual

`ChatOptions.ResponseFormat` is absent when the underlying `IChatClient` is invoked.

### Questions

1. Is `ChatClientAgent` expected to propagate `AgentRunOptions.ResponseFormat` into the underlying `ChatOptions.ResponseFormat`?
2. Should `RunAsync()` result in the same provider-level structured-output configuration as directly setting `ChatOptions.ResponseFormat`?
3. Is there a supported workaround that preserves the `ChatClientAgent` pipeline while ensuring `ResponseFormat` reaches the underlying client?

### Code Sample

```markdown
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

// The following is the error case
public sealed class CapturingChatClient : DelegatingChatClient
{
public ChatOptions? LastOptions { get; private set; }

public CapturingChatClient(IChatClient inner)
: base(inner)
{
}

public override Task GetResponseAsync(
IEnumerable messages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
LastOptions = options?.Clone();

return InnerClient.GetResponseAsync(
messages,
options,
cancellationToken);
}
}

public sealed record StructuredResponse(
string Message,
bool Success);

public static async Task ReproAsync(IChatClient rawClient)
{
var capturingClient = new CapturingChatClient(rawClient);

var agent = capturingClient.AsAIAgent(
name: "structured-output-repro",
instructions: "Return the requested structured response.");

var responseFormat =
ChatResponseFormat.ForJsonSchema();

var runOptions = new AgentRunOptions
{
ResponseFormat = responseFormat
};

await agent.RunAsync(
"Return Message='hello' and Success=true.",
options: runOptions);

Console.WriteLine(
capturingClient.LastOptions?.ResponseFormat is null
? "BUG: ResponseFormat was dropped"
: "OK: ResponseFormat reached IChatClient");
}

// The following is the working control case

public static async Task DirectChatClientControlAsync(
IChatClient chatClient)
{
var options = new ChatOptions
{
ResponseFormat =
ChatResponseFormat.ForJsonSchema()
};

await chatClient.GetResponseAsync(
[
new ChatMessage(
ChatRole.User,
"Return Message='hello' and Success=true.")
],
options);

// ResponseFormat reaches the underlying provider correctly here.
}
```

### Error Messages / Stack Traces

```markdown

```

### Package Versions

Microsoft.Agents.AI 1.19.0 and 1.21.0

### .NET Version

10.0.203

### Additional Context

_No response_

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.