Skip to content

Custom plugin tools receive undefined args when AI omits optional parameters (same root cause as #9020) #20019

@gilisho

Description

@gilisho

Description

Custom tools defined via @opencode-ai/plugin receive undefined for optional args when the AI model omits them. This is the same root cause as #9020, which was fixed for MCP tools in #11203 but not for custom plugin tools.

Steps to Reproduce

  1. Define a custom tool with an optional parameter that has a Zod .default():
import { tool } from "@opencode-ai/plugin";
import { randomUUID } from "crypto";

export default tool({
  description: "Generate one or more UUID v4s.",
  args: {
    count: tool.schema.number().min(1).max(10).default(1).describe("Number of UUIDs to generate."),
  },
  async execute(args) {
    return Array.from({ length: args.count }, () => randomUUID()).join("\n");
  },
});
  1. When the AI calls this tool without specifying count, the tool receives { count: undefined } (or {})
  2. The Zod .default(1) is never applied, so args.count is undefined
  3. Array.from({ length: undefined }) produces an empty array, and the tool returns ""

Expected Behavior

args.count should be 1 (the Zod default), or at minimum the args should be coerced from undefined to {} so Zod can apply defaults during parsing.

Actual Behavior

args.count is undefined. The Zod .default() is never triggered because the args are passed directly to execute() without being parsed through the Zod schema.

Context

The fix in #11203 patched this for MCP tools in packages/opencode/src/mcp/index.ts:

- arguments: args as Record<string, unknown>,
+ arguments: (args || {}) as Record<string, unknown>,

But custom plugin tools go through a different code path (likely packages/opencode/src/tool/) where the same coercion was not applied.

Workaround

Handle undefined manually in the execute function:

async execute(args) {
  const count = args.count ?? 1;
  return Array.from({ length: count }, () => randomUUID()).join("\n");
},

Environment

  • OpenCode version: 1.2.27
  • Model: Claude Sonnet 4.6

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions