Skip to content

Python Anthropic: signature-only thinking blocks replay as invalid null thinking #5783

@rg-ve

Description

@rg-ve

Summary

The Python Anthropic provider can serialize a signature-only reasoning content item as an invalid Anthropic thinking block during post-tool replay.

When a Content.from_text_reasoning(text=None, protected_data=...) item is included in an assistant message, _prepare_message_for_anthropic() currently emits:

{ "type": "thinking", "thinking": null, "signature": "..." }

Anthropic rejects a follow-up request with:

messages.1.content.0.thinking.thinking: Input should be a valid string

This can occur after tool use when adaptive thinking is enabled and prior assistant/tool messages are replayed into a follow-up model request.

No-network repro shape

This reproduces in provider serialization without calling Anthropic:

from agent_framework import Content, Message
from agent_framework.anthropic import AnthropicClient

client = AnthropicClient(api_key="dummy", model="claude-opus-4-7")
call_id = "toolu_probe"
messages = [
    Message(role="user", contents=["Use the lookup tool."]),
    Message(
        role="assistant",
        contents=[
            Content.from_text_reasoning(text=None, protected_data="sig_probe"),
            Content.from_function_call(call_id=call_id, name="lookup", arguments={"id": "1"}),
        ],
    ),
    Message(role="tool", contents=[Content.from_function_result(call_id=call_id, result="ok")]),
]

prepared = client._prepare_messages_for_anthropic(messages)
assert prepared[1]["content"][0]["type"] == "thinking"
assert prepared[1]["content"][0]["thinking"] is None

Expected behavior

The provider should not emit Anthropic thinking blocks where thinking is not a string.

For a signature-only reasoning item, likely behavior is either:

  1. attach the signature to the preceding thinking block when one exists and does not already have a signature, or
  2. skip the orphan signature-only reasoning item rather than serializing thinking: null.

Local validation

A local patch that skips text_reasoning items with text is None, attaching protected_data to the previous thinking block when possible, makes the no-network serialization repro stop emitting null thinking.

Focused test result:

python/packages/anthropic/tests/test_anthropic_client.py -k "text_reasoning or signature_delta"
5 passed

Related issues

This is separate from:

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions