Skip to content

CodeExecutionToolResultBlock.ToParam() drops stdout/stderr/return_code via omitzero (same class as #242, #317) #322

@goalsoft

Description

@goalsoft

Description

CodeExecutionToolResultBlock.ToParam() produces a malformed CodeExecutionToolResultBlockParam when the API returns a successful code execution result. The stdout, stderr, and return_code fields are dropped during re-serialization (when they contain zero values), causing a 400 from the API on the next agentic-loop iteration.

Error from API:

messages.21.content.1.code_execution_tool_result.content.RequestCodeExecutionToolResultError.type: Input should be 'code_execution_tool_result_error'

(The misleading error name RequestCodeExecutionToolResultError is because with all result fields dropped, the API falls back to parsing the now-empty content against the error-variant schema and fails on its required type discriminator.)

Root cause

In messageutil.go L345-364:

func (r CodeExecutionToolResultBlock) ToParam() CodeExecutionToolResultBlockParam {
    var p CodeExecutionToolResultBlockParam
    p.Type = r.Type
    p.ToolUseID = r.ToolUseID
    if r.Content.JSON.ErrorCode.Valid() {
        p.Content.OfRequestCodeExecutionToolResultError = &CodeExecutionToolResultErrorParam{
            ErrorCode: r.Content.ErrorCode,
        }
    } else {
        p.Content.OfRequestCodeExecutionResultBlock = &CodeExecutionResultBlockParam{
            ReturnCode: r.Content.ReturnCode,
            Stderr:     r.Content.Stderr,
            Stdout:     r.Content.Stdout,
        }
        // ...
    }
    return p
}

CodeExecutionResultBlockParam fields are tagged json:"...,omitzero". When a code execution returns zero/empty values (very common: successful runs often have ReturnCode=0, empty Stderr, or empty Stdout), omitzero drops the fields entirely. The API then rejects the request because the content object becomes {"type": "code_execution_result"} only, which doesn't match any valid variant shape.

Reproduction

  1. Multi-turn conversation with claude-opus-4-7 and extended thinking (adaptive mode with display: omitted)
  2. Trigger built-in web_search that leads Claude to use code_execution for analysis
  3. On the next turn (replaying the assistant message containing the code_execution_tool_result block), API rejects with the error above.

Reliably reproducible in production when Call Face-style code-review agents loop through 20+ tool calls with thinking enabled.

Same class of bug

All three tools share the same pattern: ToParam() unconditionally assigns fields that have omitzero tags, and zero values silently disappear. Suggest auditing all *ToolResultBlock.ToParam() methods for this pattern.

Workaround (adapted from #317)

import "github.com/anthropics/anthropic-sdk-go/packages/param"

content := msg.ToParam().Content
for i, block := range msg.Content {
    switch block.AsAny().(type) {
    case anthropic.CodeExecutionToolResultBlock,
         anthropic.WebSearchToolResultBlock,
         anthropic.ToolSearchToolResultBlock:
        if i < len(content) {
            content[i] = param.Override[anthropic.ContentBlockParamUnion](block.RawJSON())
        }
    }
}

Environment

  • SDK version: v1.37.0 (bug present, confirmed in multi-turn loops)
  • Go version: 1.24
  • Model: claude-opus-4-7 (triggers this more often due to server-tool + extended thinking integration)

Suggestion

Drop the omitzero tags on required fields in *ResultBlockParam structs, or explicitly set api:"required" and have ToParam() skip the omitzero behavior. A cleaner long-term fix would be for all ToParam() methods on server tool result blocks to use raw JSON passthrough internally (similar to the workaround above) — since the response shape is already server-produced and server-validated.

/cc #242 #317 — same bug class, systematic issue in ToParam() pattern

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