Tools with array/object parameters fail when LLM sends stringified JSON #4546

Open
opened 2026-02-16 17:44:32 -05:00 by yindo · 1 comment
Owner

Originally created by @crottolo on GitHub (Jan 9, 2026).

Originally assigned to: @thdxr on GitHub.

Bug Description

Tools with typed parameters (array, object, number, boolean) fail with Zod validation error when the LLM sends the parameter as a string instead of the proper type.

Error Examples

Case 1: Array as string (Todowrite)

⚙ Todowrite [todos=[{"id": "1", "content": "Database: Migration", "status": "completed", "priority": "high"}, ...]]

Error: The Todowrite tool was called with invalid arguments: [
  {
    "expected": "array",
    "code": "invalid_type",
    "path": ["todos"],
    "message": "Invalid input: expected array, received string"
  }
]

Case 2: Number as string (Bash timeout)

⚙ Bash [command=pnpm build 2>&1 | head -100, description=Build project to verify i18n fixes, timeout=180000]

Error: The Bash tool was called with invalid arguments: [
  {
    "expected": "number",
    "code": "invalid_type",
    "path": ["timeout"],
    "message": "Invalid input: expected number, received string"
  }
]

Root Cause

LLMs sometimes serialize typed parameters as strings:

  • { "todos": "[{...}]" } instead of { "todos": [{...}] }
  • { "timeout": "180000" } instead of { "timeout": 180000 }
  • { "enabled": "true" } instead of { "enabled": true }

Affected Tools

Tool Parameter Type
Todowrite todos array
Question questions array
Bash timeout number
Webfetch timeout number
Websearch numResults number
LSP tools line, character number
Edit replaceAll boolean
Multiedit replaceAll boolean

Proposed Fix

Add automatic coercion in Tool.define():

  1. If validation fails with type mismatch, attempt to coerce string values
  2. For strings starting with [ or {: try JSON.parse()
  3. For numeric strings: convert with Number()
  4. For "true"/"false": convert to boolean
  5. Retry validation with coerced values
  6. Backward compatible: if coercion fails, throw original error

Environment

  • OpenCode version: 1.1.8
  • All providers affected (Anthropic, OpenAI, Google, etc.)
Originally created by @crottolo on GitHub (Jan 9, 2026). Originally assigned to: @thdxr on GitHub. ## Bug Description Tools with typed parameters (array, object, number, boolean) fail with Zod validation error when the LLM sends the parameter as a string instead of the proper type. ## Error Examples ### Case 1: Array as string (Todowrite) ``` ⚙ Todowrite [todos=[{"id": "1", "content": "Database: Migration", "status": "completed", "priority": "high"}, ...]] Error: The Todowrite tool was called with invalid arguments: [ { "expected": "array", "code": "invalid_type", "path": ["todos"], "message": "Invalid input: expected array, received string" } ] ``` ### Case 2: Number as string (Bash timeout) ``` ⚙ Bash [command=pnpm build 2>&1 | head -100, description=Build project to verify i18n fixes, timeout=180000] Error: The Bash tool was called with invalid arguments: [ { "expected": "number", "code": "invalid_type", "path": ["timeout"], "message": "Invalid input: expected number, received string" } ] ``` ## Root Cause LLMs sometimes serialize typed parameters as strings: - `{ "todos": "[{...}]" }` instead of `{ "todos": [{...}] }` - `{ "timeout": "180000" }` instead of `{ "timeout": 180000 }` - `{ "enabled": "true" }` instead of `{ "enabled": true }` ## Affected Tools | Tool | Parameter | Type | |------|-----------|------| | Todowrite | todos | array | | Question | questions | array | | Bash | timeout | number | | Webfetch | timeout | number | | Websearch | numResults | number | | LSP tools | line, character | number | | Edit | replaceAll | boolean | | Multiedit | replaceAll | boolean | ## Proposed Fix Add automatic coercion in `Tool.define()`: 1. If validation fails with type mismatch, attempt to coerce string values 2. For strings starting with `[` or `{`: try `JSON.parse()` 3. For numeric strings: convert with `Number()` 4. For "true"/"false": convert to boolean 5. Retry validation with coerced values 6. Backward compatible: if coercion fails, throw original error ## Environment - OpenCode version: 1.1.8 - All providers affected (Anthropic, OpenAI, Google, etc.)
Author
Owner

@github-actions[bot] commented on GitHub (Jan 9, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #1373: AI_InvalidToolInputError: Invalid input for tool todowrite - Shows the exact same problem where LLM sends stringified JSON array instead of proper array format for the todos parameter
  • #7484: Question tool fails to parse JSON with French accented characters - Related validation issue where array parameters are incorrectly parsed as strings

Feel free to ignore if your specific case is different from these.

@github-actions[bot] commented on GitHub (Jan 9, 2026): This issue might be a duplicate of existing issues. Please check: - #1373: AI_InvalidToolInputError: Invalid input for tool todowrite - Shows the exact same problem where LLM sends stringified JSON array instead of proper array format for the `todos` parameter - #7484: Question tool fails to parse JSON with French accented characters - Related validation issue where array parameters are incorrectly parsed as strings Feel free to ignore if your specific case is different from these.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#4546