Skip to content

ToParam() serialization broken for web_search_tool_result and web_fetch_tool_result in multi-turn #346

@jshiv

Description

@jshiv

Bug

Message.ToParam() produces invalid request bodies for multi-turn conversations that include server-side tool results (web_search_tool_result and web_fetch_tool_result). The first turn succeeds (the API executes the tool and returns results), but the second turn fails with a 400 when the conversation history containing those results is sent back.

SDK version

v1.45.0 (also reproduced on v1.41.0)

Errors

web_search:

messages.1.content.1.web_search_tool_result.content.list[RequestWebSearchResultBlock]: Input should be a valid array

web_fetch:

messages.1.content.2.web_fetch_tool_result.content: Field required

Reproduction

Minimal Go program that triggers it (using the agent loop pattern from the SDK README):

// Any multi-turn agent loop that:
// 1. Sends a message with web_search or web_fetch tools enabled
// 2. Gets a response with server-side tool results
// 3. Calls msg.ToParam() to append the response to conversation history
// 4. Sends the next request with that history
// will fail on step 4.

I'm hitting this via cronicle's agent loop, which does:

conversation = append(conversation, msg.ToParam())  // line 323 of agent.go
// ... dispatch tool_use blocks ...
conversation = append(conversation, anthropic.NewUserMessage(results...))
// next iteration: Create request with conversation → 400

Root cause (from reading the SDK source)

WebSearchToolResultBlockContentUnion.ToParam() (messageutil.go:254):
The response union has OfWebSearchResultBlockArray which gets mapped to OfWebSearchToolResultBlockItem on the param side. The param struct serializes via param.MarshalUnion, but the API expects the content field to contain {"list": [...]} wrapping. The current serialization appears to produce a shape the API rejects.

WebFetchToolResultBlock.ToParam():
The content field is not being set at all in the param, so the API returns content: Field required.

Workaround

None currently — any agent loop that uses web_search or web_fetch and needs more than one turn will fail. The tools work fine on a single-turn request.

Environment

  • Go 1.24
  • macOS (also reproduced on Debian bookworm in k8s)
  • Model: claude-sonnet-4-6
  • Both web_search and web_fetch affected independently

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions