What Model are you using?
Describe the bug
When a Pydantic validation error occurs during a structured extraction call using ANTHROPIC_TOOLS or ANTHROPIC_REASONING_TOOLS mode via AWS Bedrock, the retry mechanism (reask_anthropic_tools) fails on every subsequent attempt with HTTP 400. The error is:
Error code: 400 - {'message': 'messages.1.content.1.tool_use.caller: Input should be a valid dictionary or object to extract fields from'}
Root cause: anthropic==0.88.0 added a new caller: Optional[Caller] = None field to ToolUseBlock. Bedrock does not populate this field — it returns caller=None. In reask_anthropic_tools, the failed completion is serialized via plain model_dump(), which produces "caller": null in the assistant message. The Anthropic API rejects null for this field.
# providers/anthropic/utils.py — reask_anthropic_tools, line 168
for content in response.content:
assistant_content.append(content.model_dump()) # ← includes "caller": null for Bedrock responses
Why it only affects Bedrock: The direct Anthropic API correctly populates caller=DirectCaller(type='direct') on tool use responses, so model_dump() produces a valid dict. Bedrock returns caller=None, so model_dump() produces "caller": null, which the API rejects.
Verified with live calls to both providers using identical code and the same impossible validation constraint (value: int = Field(ge=10, le=5)) to force retries:
# Direct Anthropic API — generation 2 response:
ToolUseBlock(caller=DirectCaller(type='direct'), ...)
# model_dump() → {"caller": {"type": "direct"}, ...} ✓ retries succeed (Pydantic errors only)
# Bedrock — generation 2 response:
ToolUseBlock(caller=None, ...)
# model_dump() → {"caller": null, ...} ✗ generation 3 fails with HTTP 400
On direct Anthropic API, all 3 generations retry successfully and only fail due to the impossible Pydantic constraint — proving the retry mechanism itself works when caller is populated. On Bedrock, generation 3 immediately returns 400.
Both ANTHROPIC_TOOLS and ANTHROPIC_REASONING_TOOLS are affected since they share the same reask_anthropic_tools handler (see ANTHROPIC_HANDLERS registry in the same file).
To Reproduce
import instructor
import anthropic
from pydantic import BaseModel, Field
class MyExtraction(BaseModel):
summary: str
value: int = Field(ge=10, le=5) # impossible constraint — forces validation failure on every generation
bedrock_client = instructor.from_anthropic(
anthropic.AnthropicBedrock(
aws_access_key="...",
aws_secret_key="...",
aws_region="us-east-1",
),
mode=instructor.Mode.ANTHROPIC_REASONING_TOOLS,
)
try:
response = bedrock_client.create(
model="us.anthropic.claude-sonnet-4-6",
max_tokens=4000,
thinking={"type": "enabled", "budget_tokens": 2000},
messages=[{"role": "user", "content": "Extract: some text"}],
response_model=MyExtraction,
)
except Exception as e:
print(type(e).__name__, str(e))
What happens step by step:
- Generation 1: Model notices the contradictory schema and refuses to call the tool →
list should have at least 1 item validation error (no ToolUseBlock yet).
- Generation 2: After reask, model calls the tool with
value=10 → Pydantic ge=10, le=5 validation fails. Bedrock returns ToolUseBlock(caller=None, ...).
reask_anthropic_tools serializes the generation 2 response via content.model_dump(), producing {"caller": null, ...} in the assistant message.
- Generation 3: API immediately returns HTTP 400 — all further retries fail identically.
Actual reproduction output (confirmed on AWS Bedrock, eu-west-1):
Generation 2 completion showing caller=None:
ToolUseBlock(
id='toolu_bdrk_015F3ce6KTnAWmxzcMFoGtdz',
caller=None, # ← Bedrock does not populate this field
input={'summary': 'some text', 'value': 10},
name='MyExtraction',
type='tool_use'
)
Generation 3 exception:
Error code: 400 - {'message': 'messages.3.content.1.tool_use.caller: Input should be a valid dictionary or object to extract fields from'}
Expected behavior
After a Pydantic validation error, the retry should succeed. The assistant message sent back to the API should not include "caller": null. The caller field should be omitted when it is None so the API accepts the retry message.
Screenshots
N/A — error is reproduced programmatically.
Proposed fix
In providers/anthropic/utils.py, reask_anthropic_tools, line 168 — use exclude_none=True:
# Before:
assistant_content.append(content.model_dump())
# After:
assistant_content.append(content.model_dump(exclude_none=True))
This strips "caller": null from Bedrock responses while leaving direct Anthropic API responses unchanged (where caller is a valid DirectCaller dict and would be preserved).
Environment:
instructor==1.15.1
anthropic==0.88.0
- Python 3.13
- AWS Bedrock (
AnthropicBedrock)
What Model are you using?
claude-sonnet-4-6via AWS Bedrock (anthropic.AnthropicBedrock)Describe the bug
When a Pydantic validation error occurs during a structured extraction call using
ANTHROPIC_TOOLSorANTHROPIC_REASONING_TOOLSmode via AWS Bedrock, the retry mechanism (reask_anthropic_tools) fails on every subsequent attempt with HTTP 400. The error is:Root cause:
anthropic==0.88.0added a newcaller: Optional[Caller] = Nonefield toToolUseBlock. Bedrock does not populate this field — it returnscaller=None. Inreask_anthropic_tools, the failed completion is serialized via plainmodel_dump(), which produces"caller": nullin the assistant message. The Anthropic API rejectsnullfor this field.Why it only affects Bedrock: The direct Anthropic API correctly populates
caller=DirectCaller(type='direct')on tool use responses, somodel_dump()produces a valid dict. Bedrock returnscaller=None, somodel_dump()produces"caller": null, which the API rejects.Verified with live calls to both providers using identical code and the same impossible validation constraint (
value: int = Field(ge=10, le=5)) to force retries:On direct Anthropic API, all 3 generations retry successfully and only fail due to the impossible Pydantic constraint — proving the retry mechanism itself works when
calleris populated. On Bedrock, generation 3 immediately returns 400.Both
ANTHROPIC_TOOLSandANTHROPIC_REASONING_TOOLSare affected since they share the samereask_anthropic_toolshandler (seeANTHROPIC_HANDLERSregistry in the same file).To Reproduce
What happens step by step:
list should have at least 1 itemvalidation error (noToolUseBlockyet).value=10→ Pydanticge=10, le=5validation fails. Bedrock returnsToolUseBlock(caller=None, ...).reask_anthropic_toolsserializes the generation 2 response viacontent.model_dump(), producing{"caller": null, ...}in the assistant message.Actual reproduction output (confirmed on AWS Bedrock,
eu-west-1):Generation 2 completion showing
caller=None:Generation 3 exception:
Expected behavior
After a Pydantic validation error, the retry should succeed. The assistant message sent back to the API should not include
"caller": null. Thecallerfield should be omitted when it isNoneso the API accepts the retry message.Screenshots
N/A — error is reproduced programmatically.
Proposed fix
In
providers/anthropic/utils.py,reask_anthropic_tools, line 168 — useexclude_none=True:This strips
"caller": nullfrom Bedrock responses while leaving direct Anthropic API responses unchanged (wherecalleris a validDirectCallerdict and would be preserved).Environment:
instructor==1.15.1anthropic==0.88.0AnthropicBedrock)