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
- Attach a hosted MCP tool to an agent backed by a reasoning model (e.g.
gpt-5.4).
- Run a multi-turn flow where the prior response is inlined into the next request (service-side storage disabled).
- 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.
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 thereasoningitem from areasoning + mcp_callpair when constructing the follow-up request.The orphaned
mcp_call— missing its pairedreasoningitem — causes Azure to reject the request with HTTP 400.Error
Steps to Reproduce
gpt-5.4).reasoning + mcp_callpair,reasoningis dropped butmcp_callis retained in the subsequent request.Root Cause
_prepare_messages_for_openaiunconditionally dropstext_reasoningitems but keepsmcp_server_tool_callitems when service-side storage is off. This splits a bound pair, producing an invalid request.Expected Behaviour
reasoningandmcp_callitems 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
reasoningis silently dropped whilemcp_callis retained → orphaned item → Azure rejects with HTTP 400.Impact
Affects all multi-turn agent flows using a reasoning model with hosted MCP tools:
WorkflowBuilderDAGs)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_filterparameter:Code Sample
Error Messages / Stack Traces
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.