Repository navigation
Description
Description
Summary
On a custom OpenAI-compatible provider (@ai-sdk/openai-compatible), a long tool-heavy session intermittently fails with:
{"error": {"message": "Assistant tool call function.arguments must be a JSON object.", "type": "invalid_request_error", "param": null, "code": null}}
Twice in one ~37-minute session (~90 assistant steps, dozens of tool calls), each failure self-heals on the next retry seconds later with unchanged history. This looks like the openai-compatible flavor of #53865 (anthropic: tool_use.input: Input should be an object).
Evidence (from session store + server log, opencode.db inspected read-only)
- Both failing requests went out in the same second the freshest assistant tool-calls message was finalized (message
time_updated== failing request timestamp: 22:02:21Z and 22:02:59Z) — the same race signature as anthropic: intermittent 400 "tool_use.input: Input should be an object" — tool input sent as raw string right after tool result #53865. - Every tool-call part stored in the session has a proper JSON-object
state.input(verified across the whole session, including missing-key edge cases). Stored history is clean; the corruption exists only in the outgoing payload — consistent with the follow-up request being built while the freshest tool call is mid-finalization/mid-argument-accumulation. - Both failures persisted as empty assistant messages with
error.type="provider.invalid-request", status 400. - Server log shows
Failed to drain SessionwithAI.Error.InvalidRequestwrapping the provider 400, once per failure. - In the installed 2.0.26 bundle, the openai-compatible chat conversion sends
function:{name, arguments:JSON.stringify(input)}, and the streaming path starts a tool call witharguments:""and appends raw text deltas (toolInputStart/toolInputDelta) until parse — so a request built mid-accumulation/finalization would carry raw argument text, which our provider's strict validator rejects with exactly this message.
Details requested in #53865 thread
- Bad
inputshape: not captured on the wire (no relay on this path); inferred non-object/invalid from the provider's "must be a JSON object" validation wording. - More than one client/server attached: possibly yes — this desktop runs multiple opencode server processes against one
~/.local/share/opencode/opencode.db(one current 2.0.26 on :49374 serving the session + three older/stale processes). Happy to test with all but one stopped if that helps narrow it. - Affected provider is strict about
tool_calls[].function.argumentsparsing to a JSON object (strings rejected). Other providers used in the same period (incl. anthropic-format and another OpenAI-compatible endpoint) logged zero such errors.
Impact
Session-generation requests fail hard until a retry lands outside the race window; with queued/drain-style runners this can surface as "failed to send message" in clients. No data loss (history is intact), but it interrupts unattended runs.
Happy to capture the outgoing request body via a pass-through relay if that would help — tell me what to log and I'll reproduce.
Plugins
[email protected], @openchamber/[email protected], @openchamber/[email protected], local see-image.js
OpenCode version
2.0.26 (npm @opencode/cli; bun standalone server)
Steps to reproduce
- Custom provider in opencode.json(c):
provider.incowithnpm: @ai-sdk/openai-compatible,options.baseURLpointing at the provider's OpenAI-compatible endpoint (host redacted) — GLM-5.3-flash-class model whose API strictly requirestool_calls[].function.argumentsto parse to a JSON object. - Run a long agentic session (30+ min, ~90 assistant steps, shell/edit tools, 2-tool-call steps) so tool results flow back continuously.
- Observe: occasional 400
invalid_request_error: Assistant tool call function.arguments must be a JSON object.on the request immediately following a tool-calls step — timestamp coincides with the freshest assistant message's finalization second. - Next attempt (retry/queued message) succeeds with unchanged history; session continues normally.
Screenshot and/or share link
No response
Operating System
Linux — Debian GNU/Linux 13 (trixie) desktop VM (client + server same host)
Terminal
OpenChamber web client attached to the background service
Activity
opencode-agent commentedon Oct 8, 2026
Thanks for the detailed report. I tried to reproduce this on 2.0.26 and couldn't.
I set up a custom provider with
@ai-sdk/openai-compatible, pointed at a local mock of the chat completions streaming API. The mock rejects any assistanttool_calls[].function.argumentsthat doesn't parse to a JSON object, with the same 400 you saw. Then I ran:shellandwritecalls, including steps with two tool calls, with arguments streamed in small pieces, all at once, or with the finish signal right after the last pieceopencode.db, including a run with slower streaming and one slower tool per stepAcross roughly 320 requests, every outgoing
argumentsvalue was a JSON object. Malformed model output was turned into an error tool result rather than being sent back raw.What would help narrow it down:
tool_calls[].function.argumentsvalue, redacted. In particular: is it empty, partial JSON, a quoted JSON string, or an array? Is there arole: "tool"message with the sametool_call_idright after it?@openchamber/opencode-cursor,@openchamber/opencode-claudeand the see-image plugins disabled? Some of these hook into requests and could change the tool-call input.opencode.dbstopped?Related: #53865 is the same symptom on the Anthropic provider (
tool_use.input: Input should be an object).rekram1-node commentedon Oct 9, 2026
Are you positive it was on 2.0.26 and not an earlier version? Can u still reproduce?
opencode-agent commentedon Oct 11, 2026
Closing this for now, since we haven't heard back. I couldn't reproduce the 400 on 2.0.26: across roughly 320 requests to a strict openai-compatible mock, every outgoing
tool_calls[].function.argumentswas a JSON object. Without the failing request body there isn't enough to go on.If it happens again, please reopen or comment here with:
argumentsvalue from the failing request (redacted), captured through a pass-through relayopencode.db#53865 tracks the same symptom on the Anthropic provider, so it's worth watching too. Thanks again for the detailed report.