Skip to content

fix(claude): collapse invalid tool_call Arguments to empty object (not just empty)#895

Open
gutopolewacz wants to merge 1 commit into
cloudwego:mainfrom
gutopolewacz:fix/claude-invalid-toolcall-args
Open

fix(claude): collapse invalid tool_call Arguments to empty object (not just empty)#895
gutopolewacz wants to merge 1 commit into
cloudwego:mainfrom
gutopolewacz:fix/claude-invalid-toolcall-args

Conversation

@gutopolewacz

Copy link
Copy Markdown

What

convSchemaMessage (components/model/claude/claude.go) only collapsed an empty tool-call Arguments to "{}" before wrapping it in json.RawMessage. A non-empty but invalid Arguments — typically a streamed tool_use truncated mid-payload (the input_json_delta chunks are cut before the JSON object closes) — was passed through raw, so ToolUseBlockParam.MarshalJSON failed on the next request re-assembly:

[NodeRunError] create new streaming message fail: ... *anthropic.ToolUseBlockParam:
json: error calling MarshalJSON for type json.RawMessage: unexpected end of JSON input

This is the same crash as #462 (empty arg, already fixed by the if args == "" guard) and the same family as #304 — but for the invalid/truncated sub-case, which the guard didn't cover. Closes #894.

How

Generalize the existing guard to any non-valid-JSON Arguments via json.Valid:

if args == "" || !json.Valid([]byte(args)) {
    args = "{}"
}

Valid JSON passes through unchanged; empty/invalid both collapse to a valid empty object so marshaling succeeds and the run continues (the model gets a normal tool result and can retry) instead of crashing. encoding/json is already imported.

Tests

Added Test_convSchemaMessage_ToolCallArguments covering: valid args pass through, empty → {}, truncated → {}, malformed → {}, and asserting the assembled MessageParam marshals without error.

Note: I couldn't run the package test binary locally — mockey v1.2.13 fails to build on my toolchain (go1.26 / darwin-arm64: relocation target runtime.duffcopy not defined), which is unrelated to this change (it fails for any test in the package). The non-test package builds and go vet is clean; CI should exercise the new test on a compatible toolchain. The new test does not use mockey.

Why it helps others

We hit this in production in a Go agent harness on Eino's DeepAgent: an agent asked to write a large JSON file in one write_file had its tool_use stream truncate, crashing the whole turn. We worked around it downstream by wrapping the public model interface and sanitizing Arguments on the model boundary — but that's a per-consumer patch for what is a one-line generalization here. Fixing it upstream protects every Eino+Claude streaming user against the same crash, exactly as the empty-arg guard already does.

@CLAassistant

CLAassistant commented Jun 24, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@gutopolewacz
gutopolewacz force-pushed the fix/claude-invalid-toolcall-args branch from 7f7d020 to e7dbd25 Compare June 24, 2026 13:17
convSchemaMessage only guarded Function.Arguments against the empty case
before wrapping it in json.RawMessage. A streamed tool_use truncated
mid-payload leaves Arguments as incomplete JSON; passing it raw to
NewToolUseBlock makes ToolUseBlockParam.MarshalJSON fail with
"unexpected end of JSON input" on request re-assembly, crashing the run.

Generalize the guard to any non-valid-JSON Arguments (json.Valid), extending
the empty-arg fix from cloudwego#462 to the invalid/truncated case. Valid JSON passes
through unchanged.

Fixes cloudwego#894
@gutopolewacz
gutopolewacz force-pushed the fix/claude-invalid-toolcall-args branch from e7dbd25 to 34ee940 Compare July 14, 2026 16:06
@gutopolewacz

Copy link
Copy Markdown
Author

Rebased onto latest main and resolved the conflict in claude_test.go (both TestVertexServiceAccountJSON and the new Test_convSchemaMessage_ToolCallArguments now coexist). The fix in claude.go is unchanged. CI was green before the rebase — would appreciate a review when someone has a moment. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

fix(claude): convToolCalls crashes with "unexpected end of JSON input" when tool_call Arguments is invalid (not just empty)

2 participants