Skip to content

Fix empty tool results from resource content (e.g. auto visualiser)#7866

Merged
DOsinga merged 3 commits intoblock:mainfrom
DOsinga:fix-tool-use-empty-crash
Mar 18, 2026
Merged

Fix empty tool results from resource content (e.g. auto visualiser)#7866
DOsinga merged 3 commits intoblock:mainfrom
DOsinga:fix-tool-use-empty-crash

Conversation

@DOsinga
Copy link
Copy Markdown
Collaborator

@DOsinga DOsinga commented Mar 13, 2026

Summary

Fixes #7613 — MCP tools that return Content::resource(...) (like the Auto Visualiser extension) produce empty tool result strings when formatted for LLM APIs, because the formatters only extracted as_text() content. This causes:

  1. Tool results appearing empty to the LLM — the model sees an empty string for tool results that actually contain data (e.g., chart HTML returned as embedded text resources)
  2. API 400 errors — Anthropic's API rejects tool_result blocks with empty content

Changes

Resource content extraction in provider formatters

  • Anthropic format: Extract text from both Content::Text and Content::Resource (embedded text resources) in tool responses
  • Snowflake format: Same fix

Empty tool result safety net in conversation repair

  • Added fix_empty_tool_results pass to the conversation repair pipeline that adds a (empty result) placeholder to tool results with no extractable text content
  • Added extract_tool_result_text helper that handles both Text and Resource content types

Agent loop cleanup

  • Simplified final_output_tool lock patterns in the agent loop (cosmetic, same behavior)

Testing

  • Added tests for tool responses with resource content (Content::embedded_text)
  • Added tests for mixed text + resource content
  • Added test for the empty tool result placeholder
  • Updated existing tests that used empty CallToolResult::success(vec![]) to use realistic content

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5977877466

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +153 to +154
if let rmcp::model::ResourceContents::TextResourceContents { text, .. } = &r.resource {
return Some(text.clone());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Serialize blob tool resources in Anthropic tool_result content

fix_empty_tool_results now considers ResourceContents::BlobResourceContents non-empty via extract_text_from_resource, so blob-only tool results skip placeholder injection, but this formatter only extracts TextResourceContents and drops blob resources. In that case the emitted Anthropic tool_result content is still an empty string, which is the same payload shape Anthropic rejects for empty tool results; this will still fail requests when tools return blob-backed resources (for example base64-encoded file reads).

Useful? React with 👍 / 👎.

@DOsinga DOsinga changed the title Fix crash on empty tool results and handle resource content in tool responses Fix empty tool results from resource content (e.g. auto visualiser) Mar 13, 2026
Copy link
Copy Markdown
Collaborator

@jh-block jh-block left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm but the build seems to be failing

@DOsinga DOsinga force-pushed the fix-tool-use-empty-crash branch from 5977877 to 27077db Compare March 18, 2026 14:36
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27077dbcb0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +47 to +51
if let Some(r) = c.as_resource() {
if let rmcp::model::ResourceContents::TextResourceContents {
text,
..
} = &r.resource
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle blob resources in Snowflake tool responses

This formatter only extracts TextResourceContents from Content::resource(...), so BlobResourceContents still produce an empty text payload here. Because fix_empty_tool_results now treats blob-backed resources as non-empty, those tool results no longer get a fallback placeholder, and Snowflake requests can silently drop blob-only tool responses (the message is skipped when text_content stays empty), so the model never sees the tool output.

Useful? React with 👍 / 👎.

Comment on lines +344 to +347
let text = extract_tool_result_text(&result.content);
if text.is_empty() {
// Add a placeholder text content so the tool result isn't empty
result.content.push(Content::text("(empty result)"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid labeling image-only tool results as empty

The new empty-result repair path adds (empty result) whenever extracted text is empty, but extract_tool_result_text only looks at text/resource content and ignores image content. That means image-only tool results are now rewritten as “empty” even when they contain real data, which can mislead downstream model reasoning in providers that already serialize tool images (for example, Google/OpenAI formatters).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f16674c4df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fn has_tool_result_content(content: &[Content]) -> bool {
content
.iter()
.any(|c| c.as_text().is_some() || c.as_resource().is_some() || c.as_image().is_some())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check tool result payload text, not just content variant

has_tool_result_content currently treats any Content::text(...) or Content::resource(...) as non-empty even when the extractable text is "", so fix_empty_tool_results skips adding the placeholder for cases like empty text output or an empty embedded text resource (e.g., reading a 0-byte file). The downstream formatter logic in format_messages (Anthropic and Snowflake) filters out empty extracted strings, so these responses still serialize to empty tool-result content and can still trigger Anthropic 400s or dropped Snowflake tool output.

Useful? React with 👍 / 👎.

- Use extract_text_from_resource in anthropic/snowflake formatters to
  handle BlobResourceContents (not just TextResourceContents)
- Check for image content in fix_empty_tool_results so image-only tool
  results aren't incorrectly labeled as empty
@DOsinga DOsinga force-pushed the fix-tool-use-empty-crash branch from f16674c to 0b67387 Compare March 18, 2026 18:39
@DOsinga DOsinga added this pull request to the merge queue Mar 18, 2026
Merged via the queue into block:main with commit 0cd41be Mar 18, 2026
21 checks passed
@DOsinga DOsinga deleted the fix-tool-use-empty-crash branch March 18, 2026 19:07
michaelneale added a commit that referenced this pull request Mar 19, 2026
* origin/main:
  fix(openai): use Responses API for gpt-5.4 (#7982)
  Remove lead/worker provider (#7989)
  chore(release): release version 1.28.0 (#7991)
  Fix empty tool results from resource content (e.g. auto visualiser) (#7866)
  Separate SSE streaming from POST work submission (#7834)
  fix: include token usage in Databricks streaming responses (#7959)
  Optimize tool summarization (#7938)
lifeizhou-ap added a commit that referenced this pull request Mar 20, 2026
* main: (22 commits)
  feat: add gemini-acp provider, update docs on subscription models + improvements to codex (#8000)
  fix(openai): use Responses API for gpt-5.4 (#7982)
  Remove lead/worker provider (#7989)
  chore(release): release version 1.28.0 (#7991)
  Fix empty tool results from resource content (e.g. auto visualiser) (#7866)
  Separate SSE streaming from POST work submission (#7834)
  fix: include token usage in Databricks streaming responses (#7959)
  Optimize tool summarization (#7938)
  fix: overwrite the deprecated googledrive extension config (#7974)
  refactor: remove unnecessary Arc<Mutex> from tool execution pipeline (#7979)
  Revert message flush & test (#7966)
  docs: add Remote Access section with Telegram Gateway documentation (#7955)
  fix: update webmcp blog post metadata image URL (#7967)
  fix: clean up OAuth token cache on provider deletion (#7908)
  fix: hard-coded tool call id in code mode callback (#7939)
  Fix SSE parsers to accept optional space after data: prefix (#7929)
  docs: add GOOSE_INPUT_LIMIT to config-files.md (#7961)
  Add WebMCP for Beginners blog post (#7957)
  Fix download manager (#7933)
  Improve the formatting of tool calls, show thinking, treat Reasoning and Thinking as the same thing (sorry Kant) (#7626)
  ...
elijahsgh pushed a commit to elijahsgh/goose that referenced this pull request Mar 21, 2026
elijahsgh pushed a commit to elijahsgh/goose that referenced this pull request Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto Visualiser extension enabled but no visualizations render inline

2 participants