Description
When an agent run pauses for tool approval and the follow-up request continues a stored response (service-side storage: previous_response_id / conversation_id), the user's function_approval_response is silently stripped from the outgoing request, so the approval decision never reaches the model. The model then re-issues the same approval request and the run pauses again — approval-gated tools can never execute under service-side storage.
Root cause: #5690 (the fix for the #3295 duplicate-item error) skips approval content under service-side storage, but the skip covers both the request and the response:
# agent_framework_openai/_chat_client.py — _prepare_message_for_openai
# (main @ c35a63e lines 1593-1595; identical in the 1.10.1 wheel)
case "function_approval_response" | "function_approval_request":
if request_uses_service_side_storage:
continue
Skipping function_approval_request is correct — the server already has it as part of the stored response. But function_approval_response is new client-side input the server has never seen, so dropping it loses the user's decision.
- Expected: the continuation request carries the approval item (
mcp_approval_response) and the approved tool executes.
- Actual: the continuation request carries no approval item; the model re-issues the approval request → infinite approve/re-ask loop.
Suggested fix: under request_uses_service_side_storage, skip only function_approval_request and keep serializing function_approval_response.
Code Sample
Serialization-only repro (no network):
from agent_framework import Content, Message
from agent_framework_openai import OpenAIChatClient
client = OpenAIChatClient(model="gpt-4o", api_key="sk-not-used")
# the user's NEW approval decision, resuming an approval-paused run
approval = Message(
"user",
[
Content.from_function_approval_response(
approved=True,
id="fa_1",
function_call=Content.from_function_call(call_id="call_1", name="do_it", arguments={}),
)
],
)
print(client._prepare_message_for_openai(approval, request_uses_service_side_storage=True))
print(client._prepare_message_for_openai(approval, request_uses_service_side_storage=False))
Output:
[]
[{'type': 'mcp_approval_response', 'approval_request_id': 'fa_1', 'approve': True}]
The same loss reproduces end-to-end against a live model with any approval_mode="always_require" tool once the request uses previous_response_id/conversation_id continuation.
Error Messages / Stack Traces
None — the item is dropped silently. The observable symptom is the model answering the approval with a fresh function_approval_request instead of running the tool.
Package Versions
agent-framework-core: 1.10.0 (also present at 1.11.0), agent-framework-openai: 1.10.0 (verified identical in 1.10.1, the latest release; still present on main @ c35a63e)
Python Version
Python 3.12
Additional Context
Is it a regression: yes — introduced by #5690 (first released in agent-framework-openai 1.4.0); before that the approval response was serialized under service-side storage. Known workaround: disable service-side storage for approval-gated agents, or re-append the stripped response after _prepare_message_for_openai.
Description
When an agent run pauses for tool approval and the follow-up request continues a stored response (service-side storage:
previous_response_id/conversation_id), the user'sfunction_approval_responseis silently stripped from the outgoing request, so the approval decision never reaches the model. The model then re-issues the same approval request and the run pauses again — approval-gated tools can never execute under service-side storage.Root cause: #5690 (the fix for the #3295 duplicate-item error) skips approval content under service-side storage, but the skip covers both the request and the response:
Skipping
function_approval_requestis correct — the server already has it as part of the stored response. Butfunction_approval_responseis new client-side input the server has never seen, so dropping it loses the user's decision.mcp_approval_response) and the approved tool executes.Suggested fix: under
request_uses_service_side_storage, skip onlyfunction_approval_requestand keep serializingfunction_approval_response.Code Sample
Serialization-only repro (no network):
Output:
The same loss reproduces end-to-end against a live model with any
approval_mode="always_require"tool once the request usesprevious_response_id/conversation_idcontinuation.Error Messages / Stack Traces
None — the item is dropped silently. The observable symptom is the model answering the approval with a fresh
function_approval_requestinstead of running the tool.Package Versions
agent-framework-core: 1.10.0 (also present at 1.11.0), agent-framework-openai: 1.10.0 (verified identical in 1.10.1, the latest release; still present on
main@ c35a63e)Python Version
Python 3.12
Additional Context
Is it a regression: yes — introduced by #5690 (first released in agent-framework-openai 1.4.0); before that the approval response was serialized under service-side storage. Known workaround: disable service-side storage for approval-gated agents, or re-append the stripped response after
_prepare_message_for_openai.