Skip to content

Python: [Bug]: ] Orphaned mcp_call items cause HTTP 400 in multi-turn reasoning model flows #6074

@swadile999

Description

@swadile999

Description

Description

When a reasoning model (e.g. gpt-5.4) is used with a hosted MCP tool in a multi-turn or multi-agent flow, the framework intermittently drops the reasoning item from a reasoning + mcp_call pair when constructing the follow-up request.

The orphaned mcp_call — missing its paired reasoning item — causes Azure to reject the request with HTTP 400.

⚠️ Intermittent: Does not fire on every run. Triggers non-deterministically, only when the model actually emits a paired reasoning + mcp_call in its response. Will surface reliably at scale in production flows.


Error

HTTP 400 — Item 'mcp_<id>' of type 'mcp_call' was provided without its
required 'reasoning' item: 'rs_<id>'.

Steps to Reproduce

  1. Attach a hosted MCP tool to an agent backed by a reasoning model (e.g. gpt-5.4).
  2. Run a multi-turn flow where the prior response is inlined into the next request (service-side storage disabled).
  3. Observe that when the model emits a reasoning + mcp_call pair, reasoning is dropped but mcp_call is retained in the subsequent request.

May require multiple runs before the issue surfaces, as the model does not always emit the paired items.


Root Cause

_prepare_messages_for_openai unconditionally drops text_reasoning items but keeps mcp_server_tool_call items when service-side storage is off. This splits a bound pair, producing an invalid request.


Expected Behaviour

reasoning and mcp_call items must be treated as a bound pair. Either both are carried forward into the next request, or both are dropped. Splitting them produces an invalid request.


Actual Behaviour

reasoning is silently dropped while mcp_call is retained → orphaned item → Azure rejects with HTTP 400.


Impact

Affects all multi-turn agent flows using a reasoning model with hosted MCP tools:

  • Sequential agent pipelines
  • Workflow graphs (WorkflowBuilder DAGs)

While intermittent per run, the probability of hitting this increases with conversation length and tool call frequency.


Workaround (caller-side)

Strip both item types at the cross-agent boundary using the public context_filter parameter:

_STRIP_CONTENT_TYPES = frozenset({
    "text_reasoning",
    "mcp_server_tool_call",
    "mcp_server_tool_result",
})

def _clean_messages(messages: list) -> list:
    """Drop reasoning + mcp_server_tool_call/result from each Message.
    If a Message ends up with no contents after filtering, drop the whole Message.
    """
    cleaned = []
    for msg in messages:
        contents = list(getattr(msg, "contents", None) or [])
        if not contents:
            cleaned.append(msg)
            continue
        kept = [c for c in contents if getattr(c, "type", None) not in _STRIP_CONTENT_TYPES]
        if not kept:
            continue  # message became empty — drop entirely
        msg.contents = kept
        cleaned.append(msg)
    return cleaned

# Apply at every AgentExecutor in the pipeline:
agent_exec = AgentExecutor(
    foundry_agent,
    context_mode="custom",
    context_filter=_clean_messages,
)

⚠️ This workaround covers the cross-agent boundary only. It does not fix the single-agent multi-turn case where the framework itself constructs the follow-up request internally.


Code Sample

Error Messages / Stack Traces

raceback (most recent call last):
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework_openai\_chat_client.py", line 685, in _get_response
    response = await client.responses.create(stream=False, **run_options)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\openai\resources\responses\responses.py", line 2626, in create
    return await self._post(
           ^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\openai\_base_client.py", line 1931, in post
    return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\openai\_base_client.py", line 1716, in request
    raise self._make_status_error_from_response(err.response) from None
openai.BadRequestError: Error code: 400 - {'error': {'message': "An error occurred invoking 'myISP_LockUnlockSolutionVersion_tool': An error occurred invoking 'myISP_LockUnlockSolutionVersion_tool'.", 'type': 'invalid_request_error', 'param': None, 'code': 'tool_user_error'}}

 

The above exception was the direct cause of the following exception:

 

Traceback (most recent call last):
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\run_sequential.py", line 340, in <module>
    sys.exit(asyncio.run(main()))
             ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\dvalluru\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\dvalluru\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\dvalluru\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 654, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\run_sequential.py", line 327, in main
    await run_pipeline(
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\run_sequential.py", line 243, in run_pipeline
    result = await workflow.run(initial_message)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_types.py", line 3157, in get_final_response
    async for _ in self:
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_types.py", line 3024, in __anext__
    update: UpdateT = await self._iterator.__anext__()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_workflow.py", line 627, in _run_core
    async for event in self._run_workflow_with_tracing(
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_workflow.py", line 398, in _run_workflow_with_tracing
    async for event in self._runner.run_until_convergence():
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_runner.py", line 124, in run_until_convergence
    await iteration_task
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_runner.py", line 210, in _run_iteration
    await asyncio.gather(*tasks)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_runner.py", line 203, in _deliver_messages
    await asyncio.gather(*tasks)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_runner.py", line 193, in _deliver_messages_for_edge_runner
    await _deliver_message_inner(edge_runner, message)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_runner.py", line 187, in _deliver_message_inner
    return await edge_runner.send_message(message, self._state, self._ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_edge_runner.py", line 147, in send_message
    await self._execute_on_target(target_id, [source_id], message, state, ctx)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_edge_runner.py", line 76, in _execute_on_target
    await target_executor.execute(
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_executor.py", line 279, in execute
    await handler(message, context)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_executor.py", line 664, in wrapper
    return await func(self, message, ctx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_agent_executor.py", line 234, in from_response
    await self._run_agent_and_emit(ctx)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_agent_executor.py", line 388, in _run_agent_and_emit
    response = await self._run_agent(cast(WorkflowContext[Never, AgentResponse], ctx))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_workflows\_agent_executor.py", line 425, in _run_agent
    response = await run_agent(
               ^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_agents.py", line 954, in _run_non_streaming
    response = await self._call_chat_client(ctx, stream=False)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework\_tools.py", line 2408, in _get_response
    await super_get_response(
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework_openai\_chat_client.py", line 687, in _get_response
    self._handle_request_error(ex)
  File "C:\Dheeraj\Customer\Accenture\ams_sca\ams_sca\.venv\Lib\site-packages\agent_framework_openai\_chat_client.py", line 586, in _handle_request_error
    raise ChatClientException(
agent_framework.exceptions.ChatClientException: ('<class \'agent_framework_foundry._agent._FoundryAgentChatClient\'> service failed to complete the prompt: Error code: 400 - {\'error\': {\'message\': "An error occurred invoking \'myISP_LockUnlockSolutionVersion_tool\': An error occurred invoking \'myISP_LockUnlockSolutionVersion_tool\'.", \'type\': \'invalid_request_error\', \'param\': None, \'code\': \'tool_user_error\'}}', BadRequestError('Error code: 400 - {\'error\': {\'message\': "An error occurred invoking \'myISP_LockUnlockSolutionVersion_tool\': An error occurred invoking \'myISP_LockUnlockSolutionVersion_tool\'.", \'type\': \'invalid_request_error\', \'param\': None, \'code\': \'tool_user_error\'}}'))

Package Versions

agent-framework==1.5.0

Python Version

3.13

Additional Context

INTERMITTENT — does not fire on every run.
Triggered only when gpt-5.4 non-deterministically emits a paired
(reasoning + mcp_call) in its response.

Metadata

Metadata

Assignees

Labels

agentsIssues related to single agentsbugSomething isn't workingpythonreproduced

Type

No fields configured for Bug.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions