Skip to content

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

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?.AdditionalPropertiesnew 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

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 <name> --local 'Ask the child what 2+2 is'. The parent reports that the tool returned Error: Function failed.

Error Messages / Stack Traces

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.<<AsAIFunction>g__InvokeAgentAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.Extensions.AI.AIFunctionFactory.ReflectionAIFunctionDescriptor.<>c__DisplayClass41_1.<<GetReturnParameterMarshaller>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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETUsage: [Issues, PRs], Target: .NettriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions