DEV Community

MilkyWay008
MilkyWay008

Posted on Originally published at github.com

Qwen Code 400 "failed to parse grammar" against llama.cpp? Here's the fix

If you run Qwen Code against a local llama.cpp server, you may have woken up one morning to every request dying with:

API Error: 400 Failed to initialize samplers: failed to parse grammar
Enter fullscreen mode Exit fullscreen mode

Before any token comes back. The model never even starts. And here's the annoying part. The same model works fine if you curl the endpoint directly, and it works fine if you're running a different model like gemma. So it's natural to blame the model, the server, the quant, whatever.

It's none of those. This is a tool-schema bug in Qwen Code, and an auto-update is what shipped it to you. Here's what's actually happening, plus the one-line settings fix that survives the updater.

What's actually breaking

llama.cpp doesn't do free-form JSON tool calling. When a client sends tool schemas, llama.cpp compiles every registered tool's JSON Schema into one big constrained-decoding grammar at sampler init. That grammar has to parse before the model generates a single token.

Qwen Code 0.22.3 registers a bunch of built-in tools, and a few of them carry string maxLength limits way past what llama.cpp's grammar converter can represent. report_findings has maxLength: 2000/4000/4096. send_message has 65536. loop_wakeup has 10000. The converter chokes, the grammar fails to parse, and you get your 400 before any generation starts.

The Qwen maintainer confirmed it. Strip those length limits and the same tool set parses clean. Include report_findings and it fails with exactly this error.

That's also why gemma "works fine". The failure isn't model-specific, it's grammar-size-dependent. Models whose tool set stays under the cap never see it. Models pulling in the heavy built-ins trip over it.

The fix: defer the tool schemas

Qwen Code ships a tools.eager setting that controls which tool schemas go into that initial request. The counterintuitive part: set it to an empty list.

{
  "tools": {
    "eager": []
  }
}
Enter fullscreen mode Exit fullscreen mode

Put that in ~/.qwen/settings.json (a project-level .qwen/settings.json works too, same shape), restart Qwen Code, done.

An empty eager list doesn't disable anything. It defers every non-exempt built-in tool schema out of the initial request, so the grammar stays small enough to parse, while the tools themselves stay registered and callable on demand. Think of it as: don't announce every tool up front, resolve them when actually used.

Two gotchas while you're in there:

  • Don't bother with "tools": { "core": [] }. It doesn't shrink the eager schemas. People still got the 400 with 23 schemas riding in the request.
  • If you're on an older workaround that used permissions.allow to shrink schemas, that's dead. permissions.allow is pure auto-approval now, it no longer affects schemas. tools.eager is the knob.

But I'm on auto-update and it keeps coming back

This is the part that stings. The broken behavior shipped in a stable auto-update (0.22.3), and the actual fix only lands in the nightly channel so far. Pin stable and you stay broken. Let it auto-update and it drags you back onto the broken build.

The merged fix (it strips the report_findings maxLength limits) is in the nightly line:

npm i -g @qwen-code/qwen-code@nightly
Enter fullscreen mode Exit fullscreen mode

Prefer stable? Then the tools.eager: [] workaround above carries you through. It's version-independent and doesn't fight the updater.

One honest caveat. The merged fix only strips the limits on report_findings. A couple of other built-in tools still carry big maxLength values (send_message at 65536, for example), so on an exotic model and server combo the grammar could still overflow. Keeping eager: [] in your settings is cheap insurance until the stable release catches up.

The pattern worth remembering

This whole class of bug is going to keep showing up as more coding agents target local llama.cpp servers. The client sends every tool schema it knows, llama.cpp has to compile them all into one grammar, and one oversized schema takes down the whole request. So if you ever see "failed to parse grammar" from a llama.cpp-based server, your first suspect shouldn't be the model. It's whatever JSON schema payload your client just sent. Check the request, look for the giant maxLength fields, and find your client's version of tools.eager to defer them.


I hit this myself running Qwen Code against a local server after an auto-update, and the error message pointed everywhere except the actual cause. The fix is tracked in qwen-code#10530 and qwen-code#10435; the upstream fix is PR #10275.

Top comments (0)