Skip to content

Python: [Bug]: Multi-turn tool calls fail with both Responses API and Chat Completions API when used via agent-framework-ag-ui (Foundry project endpoint) #5941

Closed as not planned

Description

@nomhiro

Description

Summary

Using agent-framework-ag-ui from Python with a Microsoft Foundry project inference endpoint, the first turn of a tool-calling chat works correctly, but any second or later turn that follows a tool call fails with a 400 from the upstream OpenAI API.

The failure mode differs by client type but both surface a consistent root cause: when AG-UI replays conversation history on subsequent turns, the messages produced by the prior tool-calling turn are not paired correctly with their tool results before being sent back to the LLM.

This affects all four AG-UI features we tested (Agentic Chat, Human-in-the-Loop, Shared State, Agentic Generative UI) as soon as the agent has invoked any tool at least once.

Code Sample

## Reproduction

Minimal server (full repo: <will-be-linked>):


import os
from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatClient  # or OpenAIChatCompletionClient — both fail
from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from dotenv import load_dotenv

load_dotenv()

@tool
def search_cars(car_class: str | None = None) -> list[dict]:
    return [{"car_id": "SUV-001", "class": "suv", "daily_jpy": 12800}]

client = OpenAIChatClient(  # also tried OpenAIChatCompletionClient — same outcome, different error
    model=os.environ["FOUNDRY_MODEL"],
    base_url=os.environ["FOUNDRY_BASE_URL"],  # ".../openai/v1"
    api_key=os.environ["FOUNDRY_API_KEY"],
)

agent = Agent(
    name="ShirokumaChat",
    instructions="You are a car rental booking assistant.",
    client=client,
    tools=[search_cars],
)

app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
add_agent_framework_fastapi_endpoint(app, agent, "/agentic_chat")


Steps:

1. Start the server on `localhost:8888`.
2. In AG-UI Dojo (running locally), open the Agentic Chat tab for `Microsoft Agent Framework (Python)`.
3. Send turn 1: "Show me your SUV cars" → tool is called, response streams correctly. ✅
4. Send turn 2 in the **same** chat session (anything, e.g. "What's the price?") → server returns 200 but the stream contains a `RUN_ERROR` and no text response. ❌

Error Messages / Stack Traces

## Expected behavior

Subsequent turns should continue the conversation, with the agent having access to the tool result from the previous turn.

## Actual behavior

### With `OpenAIChatClient` (Responses API)


openai.BadRequestError: Error code: 400 - {
  'error': {
    'message': 'No tool output found for function call call_cLJJ7tsAQL9j7ZK3J1g8ca30.',
    'type': 'invalid_request_error',
    'param': 'input',
    'code': None
  }
}


Full traceback (abridged):


File ".venv/Lib/site-packages/agent_framework_openai/_chat_client.py", line 647, in _stream
    async for chunk in await client.responses.create(stream=True, **run_options):
File ".venv/Lib/site-packages/openai/resources/responses/responses.py", line 2626, in create
    return await self._post(...)
agent_framework.exceptions.ChatClientException: OpenAIChatClient service failed to complete the prompt: 400 - No tool output found for function call call_cLJJ7tsAQL9j7ZK3J1g8ca30.


### With `OpenAIChatCompletionClient` (Chat Completions API)


openai.BadRequestError: Error code: 400 - {
  'error': {
    'message': "An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. The following tool_call_ids did not have response messages: call_7jOeA41CWTxLIyheCJnxqr6G, call_cWxiZjHImVV1KTq6ajOqTbnB",
    'type': 'invalid_request_error',
    'param': 'messages.[3].role',
    'code': None
  }
}


Full traceback (abridged):


File ".venv/Lib/site-packages/agent_framework_openai/_chat_completion_client.py", line 522, in _stream
    async for chunk in await client.chat.completions.create(stream=True, **options_dict):
agent_framework.exceptions.ChatClientException: OpenAIChatCompletionClient service failed to complete the prompt:
  An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'.

Package Versions

agent-framework-core 1.4.0, agent-framework-ag-ui 1.0.0rc1, agent-framework-openai 1.4.0, ag-ui-protocol 0.1.18, openai 2.37.0, fastapi 0.133.0, pydantic 2.13.4

Python Version

3.12.11

Additional Context

Frontend: AG-UI Dojo (apps/dojo in ag-ui-protocol/ag-ui, latest main), integration = Microsoft Agent Framework (Python), pointed at our local server via AGENT_FRAMEWORK_PYTHON_URL=http://localhost:8888.

Backend endpoint: Microsoft Foundry project inference URL of the form
https://<resource>.services.ai.azure.com/api/projects/<project>/openai/v1
authenticated with an API key.

Why both APIs fail (analysis)

Both errors point at the same root cause from different angles:

  • Chat Completions strictly requires (assistant{tool_calls}, tool{tool_call_id}) pairs to be interleaved in the messages array. The 400 says the tool role messages are missing from the replayed history.
  • Responses API expects function call results to be presented as function_call_output items in input. The 400 says the output for a specific call_id is missing.

In both cases, AG-UI's HttpAgent (or whatever produces the replayed history sent to the Python backend) is dropping or mis-pairing the tool result messages from the previous turn. The server-side agent-framework-ag-ui pipeline appears to pass this incomplete history through to the chat client without reconstructing the missing pairs.

Related issues

This issue intentionally reports the end-to-end symptom on Python + Foundry with both client types, because the existing issues are scattered (per-API, per-platform, per-feature) and the impression from a user's perspective is that AG-UI is currently unusable for any multi-turn tool-calling scenario in Python — which is not made visible in the AG-UI integration docs or the Microsoft Community Hub announcement post.

Requests

  1. Confirm whether the root cause is server-side (agent-framework-ag-ui history reconstruction) or client-side (AG-UI Dojo / HttpAgent not sending tool results back) — or both.
  2. Add a documentation note about the current limitation on the AG-UI integration page until fixed.
  3. Coordinate with CopilotKit#3884 / PR .NET: AF version bump 12-feb #3885 if the fix needs to land on both sides.

Workaround tried

We are evaluating the workaround suggested in CopilotKit#3884: subclass AgentFrameworkAgent to scan input messages and inject synthetic tool (or function_call_output) entries for any tool_call_id that lacks a matching result before handing off to the chat client. Will follow up here with results.

Activity

  1. added
    bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)
    on May 18, 2026
  2. added
    .NETUsage: [Issues, PRs], Target: .Net
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on May 18, 2026
  3. changed the title [-]Python: [Bug]: Multi-turn tool calls fail with both Responses API and Chat Completions API when used via `agent-framework-ag-ui` (Foundry project endpoint)[/-] [+].NET: Python: [Bug]: Multi-turn tool calls fail with both Responses API and Chat Completions API when used via `agent-framework-ag-ui` (Foundry project endpoint)[/+] on May 18, 2026
  4. removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on May 19, 2026
  5. arnabbiswas1 commented on May 27, 2026

    @arnabbiswas1

    I am encountering related error not from AgentFramework, but from Agent Service. However, it's intermittent in nature. I am creating PromptAgentDefinition Agent with local functions (FunctionTool) and the conversation is maintained using conversation_id:

    ---------------------------------------------------------------------------
    BadRequestError                           Traceback (most recent call last)
    Cell In[66], line 1
    ----> 1 response = invoke_agent_stream(
          2     ".........",
          3     conversation_id=conversation.id,
          4     openai_client=openai_client,
          5 )
          6 response.id
    
    Cell In[64], line 38, in invoke_agent_stream(prompt, conversation_id, max_hops, openai_client)
         35 if conversation_id is not None:
         36     print(f"[agent] Using conversation_id={conversation_id}")
    ---> 38 stream = openai_client.responses.create(**request)
         40 response = None
         41 printed = False
    
    File ~/<PROJECT_HOME>/venv/lib/python3.12/site-packages/openai/resources/responses/responses.py:898, in Responses.create(self, background, context_management, conversation, include, input, instructions, max_output_tokens, max_tool_calls, metadata, model, parallel_tool_calls, previous_response_id, prompt, prompt_cache_key, prompt_cache_retention, reasoning, safety_identifier, service_tier, store, stream, stream_options, temperature, text, tool_choice, tools, top_logprobs, top_p, truncation, user, extra_headers, extra_query, extra_body, timeout)
        859 def create(
        860     self,
        861     *,
       (...)    896     timeout: float | httpx.Timeout | None | NotGiven = not_given,
        897 ) -> Response | Stream[ResponseStreamEvent]:
    --> 898     return self._post(
        899         "/responses",
        900         body=maybe_transform(
        901             {
        902                 "background": background,
        903                 "context_management": context_management,
        904                 "conversation": conversation,
        905                 "include": include,
        906                 "input": input,
        907                 "instructions": instructions,
        908                 "max_output_tokens": max_output_tokens,
        909                 "max_tool_calls": max_tool_calls,
        910                 "metadata": metadata,
        911                 "model": model,
        912                 "parallel_tool_calls": parallel_tool_calls,
        913                 "previous_response_id": previous_response_id,
        914                 "prompt": prompt,
        915                 "prompt_cache_key": prompt_cache_key,
        916                 "prompt_cache_retention": prompt_cache_retention,
        917                 "reasoning": reasoning,
        918                 "safety_identifier": safety_identifier,
        919                 "service_tier": service_tier,
        920                 "store": store,
        921                 "stream": stream,
        922                 "stream_options": stream_options,
        923                 "temperature": temperature,
        924                 "text": text,
        925                 "tool_choice": tool_choice,
        926                 "tools": tools,
        927                 "top_logprobs": top_logprobs,
        928                 "top_p": top_p,
        929                 "truncation": truncation,
        930                 "user": user,
        931             },
        932             response_create_params.ResponseCreateParamsStreaming
        933             if stream
        934             else response_create_params.ResponseCreateParamsNonStreaming,
        935         ),
        936         options=make_request_options(
        937             extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
        938         ),
        939         cast_to=Response,
        940         stream=stream or False,
        941         stream_cls=Stream[ResponseStreamEvent],
        942     )
    
    File ~/vssexclude/ai_cortix/foundryiq_poc/cortix-reco-qa-gen-poc/venv/lib/python3.12/site-packages/openai/_base_client.py:1297, in SyncAPIClient.post(self, path, cast_to, body, content, options, files, stream, stream_cls)
       1288     warnings.warn(
       1289         "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
       1290         "Please pass raw bytes via the `content` parameter instead.",
       1291         DeprecationWarning,
       1292         stacklevel=2,
       1293     )
       1294 opts = FinalRequestOptions.construct(
       1295     method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options
       1296 )
    -> 1297 return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
    
    File ~/vssexclude/ai_cortix/foundryiq_poc/cortix-reco-qa-gen-poc/venv/lib/python3.12/site-packages/openai/_base_client.py:1070, in SyncAPIClient.request(self, cast_to, options, stream, stream_cls)
       1067             err.response.read()
       1069         log.debug("Re-raising status error")
    -> 1070         raise self._make_status_error_from_response(err.response) from None
       1072     break
       1074 assert response is not None, "could not resolve response (should never happen)"
    
    BadRequestError: Error code: 400 - {'error': {'message': 'No tool output found for function call call_SaZJpeD4Xp4R2L6nE9OyxCWM.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}
    
  6. added
    ag-uiUsage: [Issues, PRs], Target: AG-UI protocol integration
    and removed
    .NETUsage: [Issues, PRs], Target: .Net
    bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)
    on Jun 18, 2026
  7. changed the title [-].NET: Python: [Bug]: Multi-turn tool calls fail with both Responses API and Chat Completions API when used via `agent-framework-ag-ui` (Foundry project endpoint)[/-] [+]Python: [Bug]: Multi-turn tool calls fail with both Responses API and Chat Completions API when used via `agent-framework-ag-ui` (Foundry project endpoint)[/+] on Jul 7, 2026
  8. github-actions commented on Jul 7, 2026

    @github-actions
    Contributor

    👋 Hi shirokuma (@nomhiro) — thanks for the report!

    Your error messages ('No tool output found for function call_cLJJ7tsAQL9j7ZK3J1g8ca30' and 'tool_call_ids did not have response messages') match a confirmed bug in _sanitize_tool_history within agent-framework-ag-ui where the history sanitizer silently dropped pending tool call state when a second assistant message arrived. This was fixed in PR #6581 (commit 5145d50, merged 2026-06-19). Since you reported running agent-framework-ag-ui 1.0.0rc1, upgrading to 1.0.0rc7 or later should resolve the multi-turn tool-calling failures you observed with both the Responses API and Chat Completions API.

    • You reported using: agent-framework-ag-ui 1.0.0rc1
    • Latest version: 1.0.0rc7
    • Likely fix: 5145d50be803

    If upgrading to the latest release doesn't fix it, please reopen and we'll take another look. 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

ag-uiUsage: [Issues, PRs], Target: AG-UI protocol integrationlikely-fixedpythonUsage: [Issues, PRs], Target: Python

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions