Skip to content

fix(core): lower unparseable streaming tool input to an object - #57438

Open
chiragjangiir wants to merge 1 commit into
anomalyco:devfrom
chiragjangiir:fix/streaming-tool-input-object
Open

chiragjangiir wants to merge 1 commit into
anomalyco:devfrom
chiragjangiir:fix/streaming-tool-input-object

Conversation

@chiragjangiir

@chiragjangiir chiragjangiir commented Oct 20, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #56127

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

A tool call that is still in the streaming state retains its raw argument text. If that text is not valid JSON, toolInput in to-llm-message.ts previously returned it unchanged. The Anthropic lowerer then sent tool_use.input as a string, causing the API to reject the request with 400 tool_use.input: Input should be an object.

This change only lowers decoded arguments when they are a JSON object. Unparseable text and non-object JSON values now lower to {} while the tool call is still streaming. This preserves the object contract already used by the running, completed, and error states.

How did you verify your code works?

  • Added a regression test in packages/core/test/session-runner-message.test.ts that passes a streaming shell tool call with input: "not-json" through toLLMMessages and asserts that the resulting tool-call input is {}.
  • Confirmed that the regression test fails before the fix and passes afterward.
  • Ran the packages/core test suite. The 31 failures were identical to the pre-change baseline in this environment; no additional failures were introduced.
  • Verified that the ACP replay test passes.
  • typecheck, oxlint, and prettier checks pass.

Screenshots / recordings

Not applicable — protocol-layer fix with no UI changes.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

A tool call still in `streaming` state keeps its raw argument text. When
that text is not valid JSON, `toolInput` returned it unchanged, so the
Anthropic lowerer sent `tool_use.input` as a string and the API answered
400 "Input should be an object".

A streaming tool call now only lowers its decoded arguments when they are
a JSON object; anything else (unparseable text, or a non-object JSON
value) lowers to an empty object until the call settles, matching the
object contract the non-streaming states already enforce.

Fixes anomalyco#56127
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works. I ran the #56127 reproduction (toLLMMessages followed by the Anthropic Messages request compiler) on the base and on this branch. On the base, a streaming tool call with invalid or truncated JSON arguments produces a string tool_use.input, and JSON that parses but isn't an object ([1], 42, null) is sent as is. With this change, all of those lower to {}, while a valid JSON object is still passed through unchanged. session-runner-message.test.ts and the packages/core typecheck pass.

Public API: unchanged. The change is internal to how session history is turned into provider messages.

No blocking concerns.

Related: #51452

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

toLLMMessages emits a string tool_use.input when streaming tool arguments are not valid JSON

1 participant