Repository navigation
Description
Observed Behavior
When a function tool given to GitHubCopilotAgent produces no text, the model receives a Python object repr as the tool result:
[<agent_framework._types.Content object at 0x77287488b230>]
This happens for a tool that:
- returns
None(any tool that only does a side effect, like saving something), - returns an empty string, or
- returns only rich content, such as an image (
Content.from_data(...)).
The cause is the fallback in the tool adapter (_tool_to_copilot_tool in python/packages/github_copilot/agent_framework_github_copilot/_agent.py):
text = "\n".join(c.text for c in result if c.type == "text" and c.text)
return ToolResult(
text_result_for_llm=text or str(result),
result_type="success",
)result is the list[Content] returned by FunctionTool.invoke, so str(result) is the repr of that list. The model gets a meaningless string with a memory address instead of an empty result, and the string is different on every call.
Expected Behavior
A tool result without text is sent as an empty string (text_result_for_llm=""). This is what the Copilot SDK itself does for a handler that returns None (copilot.tools._normalize_result), and what ClaudeAgent does in the same case (it sends an empty text block).
Steps to Reproduce
- Create a
GitHubCopilotAgentwith a function tool that returnsNone,""or only an image. - Let the model call the tool (or call the converted Copilot tool handler directly, as in the script below).
- Look at
text_result_for_llmof the returnedToolResult.
Minimal Reproduction
The script only runs the tool adapter, so it needs no Copilot CLI and no login.
import asyncio
from agent_framework import Content, tool
from agent_framework.github import GitHubCopilotAgent
from copilot.tools import ToolInvocation
@tool
def save_note(text: str) -> None:
"""Save a note. Returns nothing."""
@tool
def read_note() -> str:
"""Read a note that happens to be empty."""
return ""
@tool
def get_chart() -> Content:
"""Return an image."""
return Content.from_data(data=b"\x89PNG\r\n\x1a\n", media_type="image/png")
async def main() -> None:
# No Copilot CLI is started: only the FunctionTool -> Copilot SDK tool adapter runs.
agent = GitHubCopilotAgent()
for fn, args in ((save_note, {"text": "hi"}), (read_note, {}), (get_chart, {})):
copilot_tool = agent._tool_to_copilot_tool(fn)
result = await copilot_tool.handler(ToolInvocation(arguments=args, tool_call_id="call_1"))
print(f"{fn.name}: result_type={result.result_type} text_result_for_llm={result.text_result_for_llm!r}")
asyncio.run(main())Output on main:
GitHub Copilot does not support rich tool content; dropping 1 non-text item(s) from 'get_chart'.
save_note: result_type=success text_result_for_llm='[<agent_framework._types.Content object at 0x77287488b230>]'
read_note: result_type=success text_result_for_llm='[<agent_framework._types.Content object at 0x772874917750>]'
get_chart: result_type=success text_result_for_llm='[<agent_framework._types.Content object at 0x772874990b90>]'
Error Messages and Stack Traces
No response
Package Versions
agent-framework-core: 1.25.0, agent-framework-github-copilot: 2.1.2 (source checkout of main at f145644), github-copilot-sdk: 1.0.25
Python Version
Python 3.13
Operating System
Linux
Regression
Yes
Additional Context
- Regression from Python: Unify tool results as Content items with rich content support #4331 (first released in python-1.0.0rc5). Before it,
FunctionTool.invokereturned a string (""forNone), sostr(result)was correct. Python: Unify tool results as Content items with rich content support #4331 changedinvoketo return a list ofContentitems and keptstr(result)as the fallback. I found this by reading the code before and after Python: Unify tool results as Content items with rich content support #4331; I did not run the old version. - The behavior does not depend on the operating system.
- Suggested fix: send
textwithout thestr(result)fallback. It is a one-line correction, so I'm opening a PR with it and a regression test right after this issue. - Not part of this report: images are still dropped with the "does not support rich tool content" warning. The Copilot SDK has
ToolResult.binary_results_for_llmnow, so they could be forwarded, but that is a feature and I left it out. - AI assistance: I used Claude Code as an assistant to find this bug and to draft this report. I reviewed this report myself and checked the reproduction and its output above.
Acknowledgements
- I searched existing issues and did not find a duplicate.
- I personally verified this behavior and the reproduction details are authentic.
- I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
github-actions commentedon Nov 11, 2026
Reproduced on checkout
22b5e752with an independently written script that runs only theGitHubCopilotAgent._tool_to_copilot_tooladapter. Tools returningNone,"", or only an image all producedtext_result_for_llm='[<agent_framework._types.Content object at 0x...>]'. The cause is thetext or str(result)fallback at_agent.pyline 1604.Support boundary: unclear. I found no maintained spec or doc stating the expected empty-result behavior, so a maintainer should decide. The regression cause (#4331) is from the report and was not verified. Fix PR #10272 is open and not yet released.
Run: https://github.com/microsoft/agent-framework/actions/runs/43065601988