Skip to content

Python: [Bug]: GitHubCopilotAgent sends a Python object repr as the tool result when a tool returns no text #10271

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

  1. Create a GitHubCopilotAgent with a function tool that returns None, "" or only an image.
  2. Let the model call the tool (or call the converted Copilot tool handler directly, as in the script below).
  3. Look at text_result_for_llm of the returned ToolResult.

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.invoke returned a string ("" for None), so str(result) was correct. Python: Unify tool results as Content items with rich content support #4331 changed invoke to return a list of Content items and kept str(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 text without the str(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_llm now, 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.

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Nov 11, 2026
  2. github-actions commented on Nov 11, 2026

    @github-actions
    Contributor

    Reproduced on checkout 22b5e752 with an independently written script that runs only the GitHubCopilotAgent._tool_to_copilot_tool adapter. Tools returning None, "", or only an image all produced text_result_for_llm='[<agent_framework._types.Content object at 0x...>]'. The cause is the text or str(result) fallback at _agent.py line 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

    Generated by Issue Routing for #10271 · copilot · auto · ~23 AIC · ⌖ ~1 AIC · ⊞ 5.2K · ◷

  3. added
    agentsUsage: [Issues, PRs], Target: Single agent
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: Pythonready-for-implementationMaintainers agree the problem and direction are ready for implementation.reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowtriageUsage: [Issues], Target: All issues that still need to be triaged

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions