anomalyco / anomalyco/opencode
Failed parallel tool call leaves dangling tool_call_id, causing 400 errors on all subsequent requests
@neriousy is already working on this.
Since Aug 25, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
When a batch of parallel tool calls contains a failing tool (e.g. an MCP server returning 403), opencode does not write a tool-result message for the failed tool's tool_call_id. The session history then contains an assistant message with tool_calls that has no corresponding role:"tool" response, which violates the OpenAI chat-completions protocol.
Every subsequent request in that session replays the malformed message array and is rejected by protocol-strict providers with HTTP 400. Automatic retries cannot recover because they resend the same broken history.
Error message
400 BadRequestError
Invalid request: an assistant message with 'tool_calls' must be followed by tool messages
responding to each 'tool_call_id'.
The following tool_call_ids did not have response messages:
zammad_nube_zammad_get_article_attachments:2
(Observed against a vLLM-hosted model, Kimi-K3 via LiteLLM proxy. The vLLM behavior is correct per the OpenAI spec — the client is sending an invalid messages array.)
Reproduction
- Configure an MCP tool that fails (e.g. returns 403 / auth error) alongside a working tool.
- Prompt the model so it issues parallel tool calls including both tools.
- One tool call fails (in my case another parallel call
litellm_admin_litellm_model_listreturned 403 and interrupted result flushing; the sibling callzammad_nube_zammad_get_article_attachments:2never got a result message). - Continue the conversation. Every following request fails with the 400 above; retries (3x) all fail identically. Recovery only happens after the conversation history is rebuilt.
Expected behavior
- Tool result flushing should be all-or-nothing per batch: even a failed tool call must produce a tool-result message, e.g.
{"error": "403 Auth failed: ..."}— models handle error-text results gracefully. - If a tool call is aborted/times out before producing a result, persist a
status:"error"result part instead of nothing. - Defense-in-depth: when serializing session parts to provider messages, synthesize a placeholder tool message for any dangling
tool_call_idso old/affected sessions cannot poison future requests.
Environment
- opencode 1.18.21 (per User-Agent
opencode/1.18.21 ai-sdk/provider-utils/4.0.38 runtime/bun/1.3.14) - Provider: hosted vLLM (Kimi-K3) behind LiteLLM proxy
- Tools involved: stdio MCP servers (zammad, litellm_admin)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.