Skip to content

[fix] Strip output-only citations from Claude server-tool blocks#8686

Open
vishalr15 wants to merge 1 commit into
agno-agi:mainfrom
vishalr15:fix/claude-code-execution-citations-replay
Open

[fix] Strip output-only citations from Claude server-tool blocks#8686
vishalr15 wants to merge 1 commit into
agno-agi:mainfrom
vishalr15:fix/claude-code-execution-citations-replay

Conversation

@vishalr15

@vishalr15 vishalr15 commented Jul 1, 2026

Copy link
Copy Markdown

fixes #8687

Summary

Multi-turn conversations that use Claude's code execution tool fail on the second turn with:

400 - messages.N.content.M.code_execution_tool_result.citations:
      Extra inputs are not permitted

Root cause. Anthropic emits a citations field on code_execution_tool_result blocks but rejects it on input (output and input schemas differ). agno preserves server-tool result blocks in provider_data["server_tool_blocks"] via block.model_dump() (which keeps citations), then format_messages replays those blocks verbatim on the next turn — so the illegal field is echoed straight back and the request is rejected before it reaches the model.

Fix.

  • Add dump_server_tool_block() in utils/models/claude.py: model_dump() the block, then drop the output-only fields Anthropic's input schema forbids (currently citations on code_execution_tool_result / bash_code_execution_tool_result). Use it at both capture sites in Claude._parse_provider_response and _parse_provider_response_delta. This is the root-cause fix — the field is never persisted.
  • Also call the same strip helper at the replay point in format_messages. The loop over server_tool_blocks already exists, so this adds no extra iteration, and it ensures history persisted before this fix still replays cleanly after upgrading.

The citation data itself is unaffected: it is captured separately into ModelResponse.citations. Only the redundant history-reconstruction copy — which should never be sent back as input — is pruned.

Scope is kept to the block types Anthropic is confirmed to reject (code_execution_tool_result and its bash variant); the field map is trivially extensible if other output-only fields surface.

Type of change

  • Bug fix

Checklist

  • Code complies with style guidelines (ruff format --check and ruff check pass on changed files)
  • Self-review completed
  • Documentation updated (comments, docstrings)
  • Tested in clean environment (new .venv, editable install)
  • Tests added/updated

New unit tests in tests/unit/models/anthropic/test_server_tool_block_citations.py cover the capture helper (strips citations from code-execution results, preserves unrelated blocks, idempotent) and the replay path (format_messages omits citations for already-persisted blocks). The new tests plus the existing test_citation_suppression.py all pass (17 passed).

Duplicate and AI-Generated PR Check

  • I have searched existing open pull requests and confirmed that no other PR already addresses this issue
  • This PR was drafted with AI assistance (Claude Code). The author has reviewed and understands the changes; it includes tests and passes lint/format on the touched files.

Additional Notes

The replay-side strip is a safety net for existing stored sessions; the capture-side fix means all newly stored history is clean and never needs the replay strip.

Anthropic emits a `citations` field on `code_execution_tool_result` blocks
but rejects it on input. agno preserves server-tool blocks in
`provider_data["server_tool_blocks"]` for history reconstruction and replays
them verbatim on the next turn, causing a 400 on any multi-turn conversation
that used code execution:

    messages.N.content.M.code_execution_tool_result.citations:
    Extra inputs are not permitted

Add `dump_server_tool_block()` which serializes a block and drops the
output-only fields Anthropic's input schema forbids, and use it at both
capture sites in `Claude`. Also sanitize at the replay point in
`format_messages` so history persisted before this fix replays cleanly.

The citation data itself is unaffected — it is captured separately into
`ModelResponse.citations`; only the history-reconstruction copy is pruned.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

PR Triage

Missing issue link: Please link the issue this PR addresses using fixes #<issue_number>, closes #<issue_number>, or resolves #<issue_number> in the PR description. If there is no existing issue, please create one first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude code execution: 400 on multi-turn (code_execution_tool_result.citations: Extra inputs are not permitted)

1 participant