Skip to content

Issue with Foundry hosted agents and handoff workflow pattern in agent framework #709

@dibyenduk

Description

@dibyenduk

Getting below error with hosted agents and agent framework handoff builder -

20:01:58.030 | [stream] stderr | raise TypeError(f'Object of type {o.class.name} '
20:01:58.030 | [stream] stderr | TypeError: Object of type HandoffAgentUserRequest is not JSON serializable

Gihub Repo to reproduce issue -
Github Repo Link

Analysis -
The ResponsesHostServer can't serialize the handoff tool call arguments produced by HandoffBuilder

Analysis from Copilot CLI -

The HandoffBuilder + ResponsesHostServer serialization bug works like this:

The Flow That Breaks

  1. HandoffBuilder creates a workflow where agents have auto-injected handoff_to_ tools
  2. When an agent responds (e.g., triage finishes identifying the user), the HandoffAgentExecutor calls
    ctx.request_info(HandoffAgentUserRequest(agent_response), list[Message]) to signal the workflow
  3. HandoffAgentUserRequest is a dataclass containing an AgentResponse object (which has complex nested objects like
    messages, tool calls, etc.)

Where It Crashes

In agent_framework_foundry_hosting/_responses.py:

line 484: _handle_inner_workflow()
→ for event in tracker.handle(content)
line 565: handle()
→ args_str = _arguments_to_str(content.arguments)
line 1368: _arguments_to_str()
→ return json.dumps(arguments) # 💥 BOOM

The hosting layer's event tracker tries to serialize every tool call's arguments via json.dumps() so it can
log/stream them. But content.arguments here is a HandoffAgentUserRequest dataclass — not a dict or string — so
json.dumps() raises:

TypeError: Object of type HandoffAgentUserRequest is not JSON serializable

Why It's a Bug

The _arguments_to_str() function assumes all tool call arguments are JSON-serializable (dicts, strings, numbers).
The HandoffBuilder internally uses synthetic tool calls with dataclass arguments (HandoffAgentUserRequest) to
coordinate agent handoffs. The foundry hosting package doesn't know about these special dataclass types.

It's a compatibility gap between two packages:

  • agent_framework_orchestrations (HandoffBuilder) — emits dataclass arguments
  • agent_framework_foundry_hosting (ResponsesHostServer) — expects JSON-serializable arguments

The fix would be for _arguments_to_str() to handle dataclasses (e.g., via dataclasses.asdict()) or for
HandoffAgentUserRequest to implement custom JSON serialization.

Additional Logs -

20:01:58.030 | [stream] stderr | 2026-05-14 01:01:58,028 ERROR azure.ai.agentserver: Handler raised after response.created (response_id=caresp_90837d69cbbea46100gfDe6c35dRyYXWmuKzFKIngqvUljhYcH) -- | -- | -- 20:01:58.030 | [stream] stderr | Traceback (most recent call last): 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_orchestrator.py", line 1243, in _process_handler_events 20:01:58.030 | [stream] stderr | async for raw in _iter_with_winddown(handler_iterator, ctx.cancellation_signal): 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_orchestrator.py", line 156, in _iter_with_winddown 20:01:58.030 | [stream] stderr | item = await aiter.__anext__() 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/azure/ai/agentserver/responses/hosting/_routing.py", line 347, in _await_and_normalize 20:01:58.030 | [stream] stderr | async for event in self._normalize_handler_result(inner): 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/agent_framework_foundry_hosting/_responses.py", line 484, in _handle_inner_workflow 20:01:58.030 | [stream] stderr | for event in tracker.handle(content): 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/agent_framework_foundry_hosting/_responses.py", line 565, in handle 20:01:58.030 | [stream] stderr | args_str = _arguments_to_str(content.arguments) 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/agent_framework_foundry_hosting/_responses.py", line 1368, in _arguments_to_str 20:01:58.030 | [stream] stderr | return json.dumps(arguments) 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/json/__init__.py", line 231, in dumps 20:01:58.030 | [stream] stderr | return _default_encoder.encode(obj) 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/json/encoder.py", line 200, in encode 20:01:58.030 | [stream] stderr | chunks = self.iterencode(o, _one_shot=True) 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/json/encoder.py", line 258, in iterencode 20:01:58.030 | [stream] stderr | return _iterencode(o, 0) 20:01:58.030 | [stream] stderr | ^^^^^^^^^^^^^^^^^ 20:01:58.030 | [stream] stderr | File "/usr/local/lib/python3.12/json/encoder.py", line 180, in default 20:01:58.030 | [stream] stderr | raise TypeError(f'Object of type {o.__class__.__name__} ' 20:01:58.030 | [stream] stderr | TypeError: Object of type HandoffAgentUserRequest is not JSON serializable 20:01:58.035 | [stream] stderr | 2026-05-14 01:01:58,033 ERROR opentelemetry.context: Failed to detach context 20:01:58.035 | [stream] stderr | Traceback (most recent call last): 20:01:58.035 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/opentelemetry/trace/__init__.py", line 619, in use_span 20:01:58.035 | [stream] stderr | yield span 20:01:58.035 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/opentelemetry/trace/__init__.py", line 519, in start_as_current_span 20:01:58.035 | [stream] stderr | yield span 20:01:58.035 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/agent_framework/_workflows/_workflow.py", line 402, in _run_workflow_with_tracing 20:01:58.035 | [stream] stderr | yield event 20:01:58.035 | [stream] stderr | GeneratorExit 20:01:58.035 | [stream] stderr |   20:01:58.035 | [stream] stderr | During handling of the above exception, another exception occurred: 20:01:58.035 | [stream] stderr |   20:01:58.035 | [stream] stderr | Traceback (most recent call last): 20:01:58.035 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/opentelemetry/context/__init__.py", line 155, in detach 20:01:58.035 | [stream] stderr | _RUNTIME_CONTEXT.detach(token) 20:01:58.035 | [stream] stderr | File "/usr/local/lib/python3.12/site-packages/opentelemetry/context/contextvars_context.py", line 53, in detach 20:01:58.035 | [stream] stderr | self._current_context.reset(token) 20:01:58.035 | [stream] stderr | ValueError: at 0x7f642814e240> was created in a different Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions