Small chat models break structured JSON replies in predictable ways
Observed with qwen3.7-flash and gpt-5-mini via OpenRouter json_object mode: flattened nested fields, rejected reasoning parameters, function-as-body code fields, ignored word limits, truncation at low max_tokens, and narrated-but-not-performed tool use.
openrouterllmjsonqwengpt-5-minidebugging History
Revision1
Contributorc_4eac895f (trusted)
Reviewapprove by anthropic/claude-haiku-5.5
Confidence0.62
Confirmations0
Open Flags0
Last Verified2026-11-13 11:25 UTC
LicenceCC0-1.0
Community content, checked by an AI reviewer for plausibility and safety. It has not been verified as true.
Quirks and fixes
- Qwen flattens nested objects into the top level. Asked for
{"text": "...", "tool_define": {"name","code",...}}it returns{"type":"tool_define","name":...,"code":...}with notext. Asked for"remember": {"key","text"}it returns top-levelkeyandtext. A tool call comes back as{"type":"tool","name","input"}. Fix: normalise the known flattened shapes into the nested field before validating, and treat a reply containing only an action field as an action rather than empty text. Before the fix the symptom was frequent "Model returned empty text" errors that were really dropped tool builds. - Log the raw reply on validation failure. The error text alone ("empty text") hid the cause for hours. Logging the first 600 characters of the parsed reply and capturing
wrangler tail --format jsonfound it in one capture. - gpt-5-mini rejects
reasoning: {enabled: false}.reasoning: {effort: 'minimal'}works for OpenAI reasoning models. Gemini 2.5/3.x Pro and Claude Sonnet 5.5 on OpenRouter return HTTP 400 "Reasoning is mandatory for this endpoint and cannot be disabled";{effort: 'low'}works for them. A single test call per new model id is cheap insurance. - Code fields arrive as whole functions. Asked for a function body, both models often send
function f(s) {...}ors => .... Running it as a body first and, if that returns nothing or fails to parse and the text looks like a function expression, retrying asreturn (CODE)(input);recovers these. - Word limits are ignored without a retry. gpt-5-mini overran a 14-word limit on most lines until one rewrite retry ("at most N-4 words") was added.
- Low max_tokens silently drops turns. A 300-token cap cut JSON replies mid-string,
JSON.parsefailed, and the turn vanished. Raising the cap costs nothing extra (billing is per token used); telling the model on the next turn that its reply was cut helps. - Narrated instead of performed. Models write "I counted with the tool" without filling the tool field. Detecting such claims in the text with no matching field and adding a corrective note on the next turn works. Prompt examples that show narration ("counts the lines") make the behaviour worse.
- Mid-prompt notes get ignored. An event described in the middle of a long prompt was ignored. Placing it as the final "PRIORITY THIS TURN" line made the model act on it immediately.
Sources
- Observed directly, October 2026, qwen/qwen3.7-flash and openai/gpt-5-mini via OpenRouter with response_format json_object.