microsoft / microsoft/agent-framework

.NET: [Bug]: AsAIFunction() fails under Foundry hosting: forwarded ChatHistoryProvider override conflicts with Responses-backed child agent's ConversationId

Open
#8,008 5 comments 0 reactions 1 assignee View on GitHub

@rogerbarreto is already working on this.

Since Sep 4, 2026.

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

Description

### Description

`AIAgent.AsAIFunction()` (agent-as-tool) fails when the *parent* agent runs under Foundry hosting (`AddFoundryResponses` / `MapFoundryResponses`) and the *child* agent is a Responses-backed `ChatClientAgent` created with `AIProjectClient.AsAIAgent(...)`.

Cause, as far as I can tell from the source:

1. The Foundry hosting layer runs the parent agent with an override `ChatHistoryProvider` in `AgentRunOptions.AdditionalProperties` (the Responses protocol's own history store).
2. `AsAIFunction` deliberately forwards the parent run's `AdditionalProperties` to the child run (`AgentExtensions.cs`: `FunctionInvokingChatClient.CurrentContext?.Options?.AdditionalProperties` → `new AgentRunOptions { AdditionalProperties = dict }`).
3. The child is bound to the project's Responses API, so the service returns a conversation id. `ChatClientAgent.ResolveChatHistoryProvider` then sees both a `ConversationId` and an override `ChatHistoryProvider` and throws.

Each rule is fine on its own; the combination makes the documented agent-as-tool pattern (sample `Agent_Step09_AsFunctionTool`) unusable inside a hosted agent. The same code works when the parent is run directly with `agent.RunAsync(...)` from a console app, since nothing injects the override.

Two smaller points that made this hard to diagnose:

- The function-invoking client swallows the exception and returns the tool result `Error: Function failed.` to the model, with nothing logged. I only found the cause by wrapping the function in a `DelegatingAIFunction` that catches and prints the exception.
- Forwarding the parent's `ChatHistoryProvider` to a child is arguably never desired: the child would read and write the parent's conversation history.

**Expected:** `child.AsAIFunction()` works for a Responses-backed child under Foundry hosting, or at least there is a way to opt out of the `AdditionalProperties` propagation.

**Workaround:** replace `AsAIFunction` with a hand-written `AIFunction` that calls `child.RunAsync(task, cancellationToken: ct)` with no session and no run options.

### Code Sample

```csharp
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;
using Microsoft.Extensions.AI;

var endpoint = new Uri(Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")!);
var model = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")!;
var client = new AIProjectClient(endpoint, new DefaultAzureCredential());

AIAgent child = client.AsAIAgent(
model: model,
instructions: "Answer the question briefly.",
name: "child",
description: "Answers one question.");

AIAgent parent = client.AsAIAgent(
model: model,
instructions: "Use the child tool to answer any question.",
name: "parent",
tools: [child.AsAIFunction()]);

var builder = AgentHost.CreateBuilder(args);
builder.Services.AddFoundryResponses(parent);
builder.RegisterProtocol("responses", endpoints => endpoints.MapFoundryResponses());
builder.Build().Run();
```

Run with `azd ai agent run`, then `azd ai agent invoke --local 'Ask the child what 2+2 is'`. The parent reports that the tool returned `Error: Function failed.`

### Error Messages / Stack Traces

```text
System.InvalidOperationException: Only ConversationId or ChatHistoryProvider may be used, but not both. A ConversationId indicating server-side chat history management is present (on the ChatClientAgentSession or the ChatOptions), but an override ChatHistoryProvider was provided via AdditionalProperties.
at Microsoft.Agents.AI.ChatClientAgent.ResolveChatHistoryProvider(ChatClientAgentSession session, ChatOptions chatOptions)
at Microsoft.Agents.AI.ChatClientAgent.NotifyProvidersOfNewMessagesAsync(ChatClientAgentSession session, IEnumerable`1 requestMessages, IEnumerable`1 responseMessages, ChatOptions chatOptions, CancellationToken cancellationToken)
at Microsoft.Agents.AI.ChatClientAgent.RunCoreAsync(IEnumerable`1 messages, AgentSession session, AgentRunOptions options, CancellationToken cancellationToken)
at Microsoft.Agents.AI.AIAgent.RunAsync(IEnumerable`1 messages, AgentSession session, AgentRunOptions options, CancellationToken cancellationToken)
at Microsoft.Agents.AI.AIAgentExtensions.<>c__DisplayClass1_0.<g__InvokeAgentAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Extensions.AI.AIFunctionFactory.ReflectionAIFunctionDescriptor.<>c__DisplayClass41_1.<b__12>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.Extensions.AI.AIFunctionFactory.ReflectionAIFunction.InvokeCoreAsync(AIFunctionArguments arguments, CancellationToken cancellationToken)
```

(Captured by wrapping the function in a `DelegatingAIFunction`; without that the only visible symptom is the tool result `Error: Function failed.`)

### Package Versions

- Microsoft.Agents.AI.Foundry.Hosting 1.20.0-preview.260831.1
- Microsoft.Agents.AI.Foundry 1.20.0-preview.260831.1
- Microsoft.Agents.AI 1.20.0
- Microsoft.Agents.AI.Abstractions 1.20.0
- Azure.AI.AgentServer.Core 1.0.0-beta.28
- Azure.AI.AgentServer.Responses 1.0.0-beta.8
- Azure.AI.Projects 3.0.0-beta.1

### .NET Version

.NET 10.0.100 SDK, `net10.0` target, Windows 11 (local `azd ai agent run`; not yet verified in the hosted container, but the hosting layer is the same)

### Additional Context

Not the same as #4937: there the agent itself has a `ChatHistoryProvider` configured and the request is stateless. Here the child agent has no provider of its own; the override arrives via `AsAIFunction` forwarding the hosted parent's run options.

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.