Claude API rejects tool schemas: missing 'required' field causes 'JSON schema is invalid' error #9339

Open
opened 2026-02-16 18:12:13 -05:00 by yindo · 1 comment
Owner

Originally created by @1186258278 on GitHub (Feb 14, 2026).

Originally assigned to: @thdxr on GitHub.

Bug Description

When using OpenCode with Claude models (especially thinking models like claude-opus-4-6-thinking) through an OpenAI-compatible proxy (e.g., new-api), the API returns:

***.custom.input_schema: JSON schema is invalid. It must match JSON Schema draft 2020-12

This happens because some tool schemas generated by plugins (e.g., oh-my-opencode's session_list) have properties but no required field. Claude API strictly requires required to be present when properties exists, even if it's an empty array [].

Steps to Reproduce

  1. Install OpenCode with oh-my-opencode plugin
  2. Configure a custom provider using @ai-sdk/openai-compatible pointing to a new-api instance
  3. Select claude-opus-4-6-thinking as the model
  4. Send any message

Root Cause

Two issues in packages/opencode/src/provider/transform.tsProviderTransform.schema():

1. Missing required field (primary cause)

Plugin tools like session_list generate schemas like:

{
  "type": "object",
  "properties": { "limit": { "type": "number" } },
  "additionalProperties": false
}

Claude API requires required to be present when properties exists. The thinking models are especially strict about this.

2. $schema meta field from Zod 4

Zod 4's toJSONSchema() adds "$schema": "https://json-schema.org/draft/2020-12/schema" to every schema output. While this may not always cause errors, it's a non-standard field for input_schema.

Note: The codebase already handles this for createStructuredOutputTool (line ~687 in prompt.ts: const { $schema, ...toolSchema } = input.schema), but the general ProviderTransform.schema() function doesn't strip it.

Proposed Fix

Add sanitization at the top of ProviderTransform.schema() in packages/opencode/src/provider/transform.ts:

export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JSONSchema7): JSONSchema7 {
    // Strip $schema meta field from Zod 4 toJSONSchema() output
    if ("$schema" in schema) {
      const { $schema: _, ...rest } = schema as any
      schema = rest as JSONSchema.BaseSchema
    }

    // Claude API (especially thinking models) requires "required" when "properties" exists
    const s = schema as any
    if (s.type === "object" && s.properties && !s.required) {
      s.required = []
    }

    // ... existing code ...
}

Verification

Tested by intercepting OpenCode's API requests with a proxy, isolating all 33 tools, and testing each individually:

  • session_list with original schema → 400 error
  • session_list with "required": [] added → 200 OK
  • All 33 tools together after fix → 200 OK

Environment

  • OpenCode: v1.2.1
  • oh-my-opencode: v3.5.3
  • Model: claude-opus-4-6-thinking (via new-api OpenAI-compatible proxy)
  • OS: macOS 26.2 (Apple Silicon) / Windows 11

Related

Originally created by @1186258278 on GitHub (Feb 14, 2026). Originally assigned to: @thdxr on GitHub. ## Bug Description When using OpenCode with Claude models (especially thinking models like `claude-opus-4-6-thinking`) through an OpenAI-compatible proxy (e.g., new-api), the API returns: ``` ***.custom.input_schema: JSON schema is invalid. It must match JSON Schema draft 2020-12 ``` This happens because some tool schemas generated by plugins (e.g., oh-my-opencode's `session_list`) have `properties` but no `required` field. Claude API strictly requires `required` to be present when `properties` exists, even if it's an empty array `[]`. ## Steps to Reproduce 1. Install OpenCode with oh-my-opencode plugin 2. Configure a custom provider using `@ai-sdk/openai-compatible` pointing to a new-api instance 3. Select `claude-opus-4-6-thinking` as the model 4. Send any message ## Root Cause Two issues in `packages/opencode/src/provider/transform.ts` → `ProviderTransform.schema()`: ### 1. Missing `required` field (primary cause) Plugin tools like `session_list` generate schemas like: ```json { "type": "object", "properties": { "limit": { "type": "number" } }, "additionalProperties": false } ``` Claude API requires `required` to be present when `properties` exists. The thinking models are especially strict about this. ### 2. `$schema` meta field from Zod 4 Zod 4's `toJSONSchema()` adds `"$schema": "https://json-schema.org/draft/2020-12/schema"` to every schema output. While this may not always cause errors, it's a non-standard field for `input_schema`. **Note:** The codebase already handles this for `createStructuredOutputTool` (line ~687 in prompt.ts: `const { $schema, ...toolSchema } = input.schema`), but the general `ProviderTransform.schema()` function doesn't strip it. ## Proposed Fix Add sanitization at the top of `ProviderTransform.schema()` in `packages/opencode/src/provider/transform.ts`: ```typescript export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema | JSONSchema7): JSONSchema7 { // Strip $schema meta field from Zod 4 toJSONSchema() output if ("$schema" in schema) { const { $schema: _, ...rest } = schema as any schema = rest as JSONSchema.BaseSchema } // Claude API (especially thinking models) requires "required" when "properties" exists const s = schema as any if (s.type === "object" && s.properties && !s.required) { s.required = [] } // ... existing code ... } ``` ## Verification Tested by intercepting OpenCode's API requests with a proxy, isolating all 33 tools, and testing each individually: - `session_list` with original schema → **400 error** - `session_list` with `"required": []` added → **200 OK** - All 33 tools together after fix → **200 OK** ## Environment - OpenCode: v1.2.1 - oh-my-opencode: v3.5.3 - Model: claude-opus-4-6-thinking (via new-api OpenAI-compatible proxy) - OS: macOS 26.2 (Apple Silicon) / Windows 11 ## Related - Similar issue in Claude Code: https://github.com/anthropics/claude-code/issues/586
Author
Owner

@github-actions[bot] commented on GitHub (Feb 14, 2026):

This issue appears to be a duplicate or closely related to several existing issues that report the same root cause—missing required field in tool schemas:

  • #11357: JSON Schema validation fails on empty tool parameters {} with Kimi K2.5
  • #8184: Strict JSON Schema validation fail on backends like SGLang (Missing required field in empty tool parameters)
  • #9233: LiteLLM Proxy: Vertex AI Gemini models fail for tools with empty parameters
  • #13148: Gemini schema sanitization misses properties/required when type is undefined

All of these report that when tool schemas have properties but are missing the required field, various providers (Claude via proxy, SGLang, Vertex AI Gemini, Kimi K2.5) reject the schema as invalid.

The fix you've proposed (adding required: [] when properties exists) aligns with the solutions discussed in the related issues above. This should be consolidated into a single fix in ProviderTransform.schema() that handles the schema sanitization comprehensively across all providers.

@github-actions[bot] commented on GitHub (Feb 14, 2026): This issue appears to be a duplicate or closely related to several existing issues that report the same root cause—missing `required` field in tool schemas: - #11357: JSON Schema validation fails on empty tool parameters {} with Kimi K2.5 - #8184: Strict JSON Schema validation fail on backends like SGLang (Missing required field in empty tool parameters) - #9233: LiteLLM Proxy: Vertex AI Gemini models fail for tools with empty parameters - #13148: Gemini schema sanitization misses properties/required when type is undefined All of these report that when tool schemas have `properties` but are missing the `required` field, various providers (Claude via proxy, SGLang, Vertex AI Gemini, Kimi K2.5) reject the schema as invalid. The fix you've proposed (adding `required: []` when properties exists) aligns with the solutions discussed in the related issues above. This should be consolidated into a single fix in `ProviderTransform.schema()` that handles the schema sanitization comprehensively across all providers.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#9339