Skip to content

OpenAI-compatible models can call write/edit with invalid schema arguments #29142

Closed as not planned

Description

@deffenda

Description

OpenCode intermittently invokes built-in write and edit tools with invalid argument shapes when using OpenAI-compatible models. The UI surfaces schema errors instead of recovering or prompting the model with the exact expected schema.

This causes repeated failed tool calls and can trap an agent in a loop. I have seen it with local Qwen via LM Studio, and a similar planner flow with DeepSeek V4 Flash Free when the prompt asked the model to create a task packet file.

Related/possibly duplicate issues:

This report adds concrete edit failures as well as write failures on OpenCode 1.15.10.

OpenCode version

opencode version: 1.15.10
os: Darwin 25.5.0 arm64
terminal: dumb
plugins: none

Installed as the macOS desktop app / CLI.

Model/provider setup

Observed primarily with:

  • qwen3-coder-next/qwen3-coder-next
  • OpenAI-compatible provider pointed at LM Studio, http://127.0.0.1:1234/v1

Also observed in a planner workflow using:

  • opencode/deepseek-v4-flash-free

Symptoms

Write tool failure

From captured output:

✗ Write requirements.txt failed
Error: The write tool was called with invalid arguments: SchemaError(Missing key
  at ["content"]).
Please rewrite the input so it satisfies the expected schema.

Another captured run:

Error: The write tool was called with invalid arguments: SchemaError(Missing key
  at ["content"]).
Please rewrite the input so it satisfies the expected schema.

The model appeared to send a write call using a non-matching key such as fileContent rather than the expected content key.

Edit tool failure

From captured output:

✗ Edit  failed
Error: The edit tool was called with invalid arguments: SchemaError(Missing key
  at ["filePath"]).
Please rewrite the input so it satisfies the expected schema.

This repeated many times in one session. In the same run, valid-looking edit attempts also sometimes failed with normal semantic errors such as multiple oldString matches, then subsequent attempts lost the filePath entirely and hit schema validation.

Steps to reproduce

One reliable pattern for me:

  1. Configure an OpenAI-compatible local model in OpenCode, e.g. LM Studio serving Qwen Coder.
  2. Run opencode run with a coding task that requires creating or editing files.
  3. Ask the agent to create or modify files using OpenCode's normal write/edit tools.
  4. Observe write or edit tool calls fail schema validation before execution.

Example command shape:

opencode run \
  --dir /path/to/repo \
  --agent qwen \
  --model qwen3-coder-next/qwen3-coder-next \
  "Read a local task packet and implement the listed files."

The issue appears more likely with local/OpenAI-compatible models, but the failure is surfaced by OpenCode's tool schema handling.

Expected behavior

OpenCode should either:

  • expose tool schemas to models in a way that reliably causes correct keys (content, filePath, etc.), or
  • repair/reject malformed tool calls with a clear model-visible correction, or
  • include enough diagnostics to show the malformed tool-call payload safely.

At minimum, if the model sends fileContent for write, it would be helpful to either accept common aliases or return a tool error that explicitly says write requires { filePath, content }.

Actual behavior

OpenCode displays:

The write tool was called with invalid arguments: SchemaError(Missing key at ["content"])

and/or:

The edit tool was called with invalid arguments: SchemaError(Missing key at ["filePath"])

The agent often retries and accumulates many failed tool calls. I had to work around it by disabling edit/write for local agents and making the harness write files itself from final chat output, or forcing shell heredocs.

Workaround used locally

Project agent config now denies the problematic tools:

permission:
  read: allow
  glob: allow
  grep: allow
  list: allow
  bash: allow
  edit: deny
  write: deny

For planner flows, I changed prompts so the model returns markdown between markers, then an external shell harness writes the file. For implementer flows, I force shell heredocs instead of OpenCode write / edit.

This works around the issue, but it removes one of OpenCode's core file-editing paths for local models.

Activity

  1. nikhilkulkarni1755 commented on Jun 1, 2026

    @nikhilkulkarni1755

    PR open at #30224.

  2. github-actions commented on Aug 1, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  3. VrilLabs commented on Aug 23, 2026

    @VrilLabs

    For anyone landing here from a search: there are two different causes behind this same SchemaError(Missing key ...) message, and they need different fixes. Worth checking which one you actually have before trying anything, because the fix for the second one isn't a plugin.

    Look at the recorded tool input for a failed call:

    Tool input Cause Fix
    {"fileContent": "...", "path": "..."} model used synonym keys key normalization
    {} — empty model emitted no arguments at all provider limit config

    1. Mis-keyed arguments

    This is the case in the original report — the model sends the right values under the wrong names (fileContent for content, path for filePath). The error names the missing key but never says what the model should have sent, so it retries the same shape and loops.

    This can be fixed from userland with no rebuild, because tool.execute.before runs before the schema decode and receives the live args object (in session/tools.ts, plugin.trigger("tool.execute.before", …) precedes item.execute). Renaming the key in place there makes the call succeed rather than just producing a better error.

    Plugin doing that, plus reordering the write schema so filePath is declared before content: https://github.com/VRIL-LABS/better-write-opencode — MIT, drop one file into ~/.config/opencode/plugins/.

    2. Empty {} arguments — this one is a config issue

    If the failing input is an empty object, there are no keys to rename and no plugin can help. The model degenerated and emitted a tool call with no arguments.

    In my case this traced back to auto-compaction never running. Custom providers default to limit.context = 0, and isOverflow() in session/overflow.ts returns early on exactly that:

    if (input.model.limit.context === 0) return false

    So with a custom @ai-sdk/openai-compatible provider that doesn't declare limits, compaction never fires, context grows unbounded, and the model eventually emits argument-less tool calls. Measured over one session: tool calls succeeded at ~66k input tokens on average, and failed from ~80k all the way up to 167k. Every single failure had input = {} — 70 bash, 18 write.

    The fix is to declare limits on the model:

    "models": {
      "your-model": {
        "limit": {
          "context": 262144,   // the model's real window
          "input":    80000,   // compaction trigger
          "output":   32768
        }
      }
    }

    limit.input is the useful knob here — usable = limit.input − reserved, so you can declare the true context window while still compacting early enough to stay in the range where the model reliably emits well-formed tool calls.


    Either way, none of this requires edit: deny / write: deny. Hope it saves someone the same detour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions