Python: Fix handoff target invoked with no messages when response has no text - #7576
Open
Sadok Barbouche (cr-sbarbouche) wants to merge 2 commits into
Open
Conversation
… no text HandoffAgentExecutor sent the handoff target an AgentExecutorRequest with messages=[] whenever the handing-off agent's response cleaned to an empty conversation (e.g. a response consisting solely of the handoff tool call, with no text content). Send a continuation instruction instead so agents that reject empty input are never invoked with nothing at all.
Sadok Barbouche (cr-sbarbouche)
temporarily deployed
to
github-app-auth
August 8, 2026 10:32 — with
GitHub Actions
Inactive
Sadok Barbouche (cr-sbarbouche)
temporarily deployed
to
github-app-auth
August 8, 2026 10:32 — with
GitHub Actions
Inactive
Sadok Barbouche (cr-sbarbouche)
temporarily deployed
to
github-app-auth
August 8, 2026 10:32 — with
GitHub Actions
Inactive
Sadok Barbouche (cr-sbarbouche)
temporarily deployed
to
github-app-auth
August 8, 2026 10:33 — with
GitHub Actions
Inactive
Copilot started reviewing on behalf of
Sadok Barbouche (cr-sbarbouche)
August 8, 2026 10:33
View session
Contributor
There was a problem hiding this comment.
Pull request overview
Prevents handoff targets from receiving an empty request after a textless tool-only handoff response.
Changes:
- Adds a fallback continuation instruction for empty cleaned responses.
- Adds streaming, non-streaming, and text-preservation tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
_handoff.py |
Injects the fallback handoff message. |
test_handoff.py |
Adds regression coverage for textless handoffs. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
The prior test went through HandoffBuilder's full multi-agent graph, but that graph broadcasts the initial user message to every participant before anyone runs, so the target's cache is never actually empty there - the test only checked for the injected text, not the reported failure mode. Drive HandoffAgentExecutor directly instead, so the assertion is exactly about what the fix changes: the direct request sent to the handoff target. Also correct the source comment, which claimed the target's cache is guaranteed empty when it's actually conditional on broadcast history, and drop the A2AAgent example (it can't be a HandoffBuilder participant).
Sadok Barbouche (cr-sbarbouche)
temporarily deployed
to
github-app-auth
August 8, 2026 10:44 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation & Context
HandoffAgentExecutorbroadcasts only the cleaned agent response to the other agents, then sends the handoff target anAgentExecutorRequest.clean_conversation_for_handoffdrops every message with no text content, so a response consisting solely of the handoff tool call (no text) cleans to an empty list. The handoff target's request then carriesmessages=[].AgentExecutortolerates an empty cache and only logs a warning, so most chat agents survive. Agents that require input do not — for example anA2AAgenttarget raisesValueError: At least one message is required when starting a new task (no continuation_token)., aborting the whole workflow. This is especially reachable in the handoff pattern because the handoff itself is a function call: an agent that hands off without also emitting text produces exactly the tool-only response that cleans away to nothing.This is the same root cause as #7456, in a different orchestrator. PR #7549 fixed the group-chat orchestrators by supplying a continuation instruction when the selected speaker would otherwise receive no messages; the handoff path was deliberately left out of that PR to keep it scoped to the linked issue.
Description & Review Guide
What are the major changes?
HandoffAgentExecutor._run_agent_and_emitnow sends the handoff target a continuation instruction message instead ofmessages=[]when the handing-off agent's cleaned response is empty. The instruction text lives in a private module constant,_HANDOFF_CONTINUATION_DEFAULT_INSTRUCTION, following the existing_AUTONOMOUS_MODE_DEFAULT_PROMPTpattern already in_handoff.py.What is the impact of these changes?
The fix is scoped to the case where the handoff response cleans to no messages at all. When the response has any text content, nothing changes and no instruction is injected (covered by a dedicated test). It applies to every agent type rather than special-casing A2A, so any participant that rejects empty input benefits.
What do you want reviewers to focus on?
Whether the injected instruction is the right shape versus replaying other conversation context into the request, and whether
cleaned_responsebeing empty is the correct (and only) signal to check here in the handoff path.Tests cover a handoff whose response has only the handoff tool call and no text (streaming and non-streaming), asserting the target's first invocation carries the continuation instruction, plus a companion test asserting the instruction is not injected when the handoff response already has text.
Related Issue
Fixes #7573
Contribution Checklist