Skip to content

Python: [Bug]: Anthropic redacted_thinking blocks are dropped and never replayed in tool loops #10104

Closed

Description

Observed Behavior

agent-framework-anthropic drops redacted_thinking content blocks when it parses a Claude response. When the function-invocation loop sends the assistant turn back with the tool_result, the redacted_thinking block is missing. The tool_use block, and any normal thinking block, are replayed correctly.

In _parse_contents_from_anthropic, redacted_thinking falls through to case _. That branch adds the type to request_state.dropped_content_types, logs at debug level, and returns no Content. Only the pause_turn continuation reads dropped_content_types, since #9718. The normal tool-call loop doesn't read it, so the block is lost silently.

Expected Behavior

redacted_thinking should round-trip unchanged, the same way thinking and its signature already do. Anthropic requires the thinking blocks of the last assistant turn, including redacted_thinking, to be passed back unmodified while tool use continues (Preserving thinking blocks).

The expected replay for [redacted_thinking, tool_use] is [{"type": "redacted_thinking", "data": "<opaque>"}, {"type": "tool_use", ...}].

Steps to Reproduce

  1. Install the packages listed under Package Versions.
  2. Run the script below. It is offline: it uses synthetic SDK blocks, makes no network call and needs no credentials.
  3. The control assertion (plain thinking) passes. The redacted_thinking assertion fails.

Minimal Reproduction

import json

from agent_framework import Message
from agent_framework_anthropic import AnthropicClient
from anthropic.types.beta import BetaRedactedThinkingBlock, BetaThinkingBlock, BetaToolUseBlock

client = AnthropicClient(api_key="unused-offline-test", model="claude-haiku-4-5")


def replay(blocks):
    # Real provider parsing and replay serialization; no network call.
    contents = client._parse_contents_from_anthropic(blocks)
    message = Message(role="assistant", contents=contents)
    return [b["type"] for b in client._prepare_message_for_anthropic(message)["content"]]


tool_use = BetaToolUseBlock(type="tool_use", id="toolu_test", name="lookup", input={})
thinking = BetaThinkingBlock(type="thinking", thinking="Visible summary.", signature="synthetic-signature")
redacted = BetaRedactedThinkingBlock(type="redacted_thinking", data="synthetic-opaque-data")

print(json.dumps({"thinking": replay([thinking, tool_use]), "redacted_thinking": replay([redacted, tool_use])}))
assert replay([thinking, tool_use]) == ["thinking", "tool_use"]
assert replay([redacted, tool_use]) == ["redacted_thinking", "tool_use"]  # Fails

Error Messages and Stack Traces

{"thinking": ["thinking", "tool_use"], "redacted_thinking": ["tool_use"]}
Traceback (most recent call last):
  File "maf_redacted_repro.py", line 23, in <module>
    assert replay([redacted, tool_use]) == ["redacted_thinking", "tool_use"]  # Fails
AssertionError

This report is about silent block loss in MAF's request building. I have not reproduced the resulting API error against live Claude, because redacted thinking only appears when Claude's safety systems flag the reasoning. I also ran an end-to-end check against a local mock of the Messages API that enforces the documented rule. In that check, `Agent.run` with a tool and extended thinking fails on the second request with `ChatClientInvalidRequestException` (400), in both streaming and non-streaming runs and through `Agent.as_tool`.

Package Versions

agent-framework-core==1.24.0, agent-framework-anthropic==1.0.0b261030, anthropic==1.17.0

Python Version

Python 3.13.12

Operating System

Windows

Regression

No

Additional Context

The behavior is still the same on main at 708c8f3a59bb139e532b609c344bff2a108672d1 (from reading the source):

Related, all closed: #5783 and #8168 cover thinking blocks lost before replay; #9718 added the dropped_content_types guard for pause_turn.

A possible fix, following how #9718 keeps server-tool blocks: parse redacted_thinking into a reasoning Content that carries data (for example in additional_properties["anthropic"]), and serialize it back as {"type": "redacted_thinking", "data": ...} in _prepare_message_for_anthropic. The orphan-signature skip must not drop it, and streaming aggregation must keep it as its own block. Regression coverage: round-trip [redacted_thinking, tool_use] and [thinking, redacted_thinking, tool_use], both streaming and non-streaming. If a block still can't be replayed, failing loudly before the request is better than dropping it silently.

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 4, 2026
  2. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Nov 4, 2026
  3. github-actions commented on Nov 4, 2026

    @github-actions
    Contributor

    🤖 Automated triage reproduction notes (agent-authored — trust but verify)

    Agent analysis

    Repro: python/packages/anthropic/agent_framework_anthropic/_chat_client.py::_parse_contents_from_anthropic at lines 2102-2104 drops redacted_thinking through its default branch when it precedes a tool call. Minimal repro: parse synthetic BetaRedactedThinkingBlock and BetaToolUseBlock instances, wrap the resulting contents in an assistant Message, then call _prepare_message_for_anthropic; the output contains only tool_use.

    • Failing test: python/packages/anthropic/tests/test_anthropic_client.py::test_redacted_thinking_round_trips_with_tool_use
    • Files examined: python/packages/anthropic/agent_framework_anthropic/_chat_client.py, python/packages/anthropic/tests/test_anthropic_client.py, python/packages/anthropic/pyproject.toml, docs/specs/004-python-function-calling-loop.md
    • Tests run: test_redacted_thinking_round_trips_with_tool_use, test_parse_contents_records_dropped_redacted_thinking, test_prepare_message_for_anthropic_text_reasoning_with_signature
    • Reported version: 1.0.0b261030
    • Current version: 1.0.0b261030
  4. added
    agentsUsage: [Issues, PRs], Target: Single agent
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Nov 4, 2026
  5. added a commit that references this issue on Nov 4, 2026
    42d6ea8
  6. added a commit that references this issue on Nov 5, 2026
    07b9b3f
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: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions