anomalyco / anomalyco/opencode

Client tool named `tool_search` is serialized as the OpenAI built-in tool, dropping `arguments`

Open
#42,545 0 comments 0 reactions 1 assignee View on GitHub

@kitlangton is already working on this.

Since Aug 14, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

A client side tool whose name collides with an OpenAI provider built in tool gets serialized as a provider tool item instead of a function_call, and the request is rejected:

AI_APICallError: Missing required parameter: 'input[1].arguments'.

The index varies with history length. It is always the item for the tool that was just called. The failure is not recoverable in that session, because the bad item is persisted and replayed on every later message.

I originally hit this through the opencode-tool-search plugin, which registers a client tool literally named tool_search. That is also the name of the OpenAI Responses built in tool, and the serializer matches on the name alone:

let P = Y.toProviderToolName(D.toolName)
if (P === "tool_search") {
  let y = typeof D.input === "string" ? await parse({text: D.input, schema: MQ}) : await validate({value: D.input, schema: MQ})
  M.push({type: "tool_search_call", id: ..., execution: ..., call_id: y.call_id ?? null, status: "completed", arguments: y.arguments})
  break
}

MQ is OpenAI's tool_search input schema. The client tool's input is {"query": "read files"}, which has no arguments field, so y.arguments is undefined and the emitted tool_search_call goes out without it.

Isolated it to the name with a single variable A/B. Same trivial plugin, same model, same prompt, only the tool name changes:

// works: two turns, replays cleanly
dummy_echo: tool({
  description: "Echo back the given text. Use when asked to echo.",
  args: { text: tool.schema.string().describe("text to echo") },
  async execute(args) { return `ECHO:${args.text}` },
})

// fails immediately on the first call
tool_search: tool({ /* identical body */ })
⚙ dummy_echo {"text":"hi"}     -> fine
⚙ tool_search {"text":"hi"}    -> Error: Missing required parameter: 'input[1].arguments'.

Built in tools are unaffected, and so is the same plugin on a non OpenAI provider (I use it daily against an OpenAI compatible endpoint with no trouble).

The same branch special cases local_shell, shell and apply_patch, so I would expect a client or MCP tool taking any of those names to fail the same way.

Suggested fix: gate that branch on the tool actually being provider executed rather than on the name, or reserve the provider built in names so a client tool cannot shadow one.

Possibly related: #38384 reported the identical error message with an MCP server and no plugins, and was closed without a fix. I have not confirmed it is the same cause, the name collision theory only fits if that server exposes a colliding name. Not the same as #37131, which is a missing id on an item_reference with @ai-sdk/azure.

Plugins

opencode-tool-search 0.4.3, and a hand written two line plugin for the A/B above

OpenCode version

1.18.18

Steps to reproduce
  1. opencode auth login with a ChatGPT account (the OAuth path, not an API key).
  2. Drop this in .opencode/plugin/coll.js:
import { tool } from "@opencode-ai/plugin"
export default async () => ({
  tool: {
    tool_search: tool({
      description: "Echo back the given text. Use when asked to echo.",
      args: { text: tool.schema.string().describe("text to echo") },
      async execute(args) { return `ECHO:${args.text}` },
    }),
  },
})
  1. opencode run -m openai/gpt-5.6-luna --auto "Call the tool_search tool with text 'hi'. Then stop."
⚙ tool_search {"text":"hi"}
Error: Missing required parameter: 'input[1].arguments'.
  1. Rename the tool to anything else and it works, including a second turn that replays the call.

Log line:

level=ERROR message="stream error" providerID=openai modelID=gpt-5.6-luna agent=build mode=primary
  error.error="AI_APICallError: Missing required parameter: 'input[1].arguments'."

The stored part is complete, so the input survives to the database and is dropped in the request build:

{"type":"tool","tool":"tool_search","callID":"call_BjrXpQB96jT4MSRS4TaR6YO5",
 "state":{"status":"completed","input":{"query":"read files"},"output":"Found 5 tool(s): ..."}}
Screenshot and/or share link

No response

Operating System

Ubuntu 24.04.4 LTS (aarch64, kernel 6.17.0-1019-oracle)

Terminal

Reproduced headless via opencode run over SSH, so no TUI involved

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.