LM Studio structured output on MLX: Qwen3.6 returns empty content and JSON in reasoning_content

LM Studio structured output from Qwen3.6 35B-A3B on MLX came back with empty content five times out of five. The one-command check and three ways round it.

0:00
LM Studio structured output on MLX: Qwen3.6 returns empty content and JSON in reasoning_content

The request asked for three facts about London as JSON. The reply came back with a finish reason of stop, 26 completion tokens and an empty string where the answer should be. The JSON was there, one field over, in reasoning_content. That is what LM Studio structured output returned from Qwen3.6 35B-A3B on the MLX engine on our test machine on 12 October 2026, five times out of five. The same model as a GGUF on llama.cpp answered correctly every time. This guide shows the fault, a one-command check for it, and three ways round it.

What LM Studio structured output returned from Qwen3.6 on MLX

LM Studio’s documentation for structured output over the OpenAI-compatible API says that a JSON schema passed in response_format makes the model respond in JSON that conforms to it, and that the object arrives as a string in choices[0].message.content. We sent that kind of request to four builds on an M4 Pro with 48 GB of unified memory, on LM Studio 0.4.25.

The schema had three required fields: city as a string, population as an integer and coastal as a boolean, with strict set to true and no extra properties allowed. The prompt was one line, “Give facts about London”, then the same for Lagos and Denver. Temperature was 0 and the reply cap was 2,000 tokens.

BuildEngineJSON in the reply
Qwen3.6 35B-A3B, Q4_K_M GGUFllama.cpp 2.47.03 of 3
Qwen3.6 35B-A3B, 4-bit MLXMLX 1.11.00 of 3
Gemma 4 31B, QAT Q4_0 GGUFllama.cpp 2.47.03 of 3
Gemma 4 31B, 4-bit MLXMLX 1.11.03 of 3

The three failures were streamed requests. We sent two more to the Qwen3.6 MLX build without streaming, to read the whole response object. Both returned "content": "" and this in reasoning_content:

{"city": "London", "population": 8982000, "coastal": false}

The usage block counted 15 prompt tokens and 26 completion tokens, and it classed 25 of those 26 as reasoning tokens. So the model wrote valid JSON that matched the schema. The server filed it as thinking and returned nothing as the answer.

How to check whether your LM Studio structured output is affected

One request settles it. Load the model you use, then run this with its identifier in place of the placeholder.

curl -s http://localhost:1234/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "YOUR-MODEL-IDENTIFIER",
    "messages": [{"role": "user", "content": "Give facts about London."}],
    "temperature": 0,
    "max_tokens": 2000,
    "response_format": {"type": "json_schema", "json_schema": {
      "name": "city", "strict": true, "schema": {
        "type": "object",
        "properties": {
          "city": {"type": "string"},
          "population": {"type": "integer"},
          "coastal": {"type": "boolean"}},
        "required": ["city", "population", "coastal"],
        "additionalProperties": false}}}
  }'

Look at two fields in the reply. If content holds the JSON, the build is fine. If content is empty and reasoning_content holds the JSON, you have the fault. A finish reason of stop with a very small completion count is the other sign: ours stopped at 26 tokens with a 2,000-token cap, because the server treated the schema as satisfied the moment the JSON closed.

Run the check on the exact build you serve. Our results split by model and by engine at once. Gemma 4 31B on the same MLX engine returned its JSON in content all three times, and that build wrote no reasoning text in any of our timed tests. Qwen3.6 35B-A3B writes reasoning on both engines and failed only on MLX.

Why the JSON ends up in reasoning_content

We did not read the engine’s code, so this section reports what LM Studio’s own tracker says and what our response objects show.

The behaviour has been reported more than once. Issue 1773 in the LM Studio bug tracker, opened on 9 April 2026, describes it on Qwen 3.5 reasoning models under LM Studio 0.4.10: the JSON schema constraint appears to match against the reasoning stream, generation stops when the JSON is complete, and content comes back empty. That report adds that a non-reasoning model on the same endpoint was not affected. Issue 1971, opened on 27 May 2026, reports the same result on an MLX build of a smaller Qwen model under LM Studio 0.4.8, with thinking switched off in the request. Both were still open when we read them on 12 October 2026.

Our numbers fit that description. Twenty-five of 26 completion tokens were counted as reasoning, the reasoning text was the finished JSON with no thinking around it, and generation ended there. A reasoning model normally thinks first and answers second. Here the schema seems to have been applied to the first of those two phases, and there was no second.

It also fits what we found in September, when we measured what a JSON schema does to the reasoning phase in LM Studio on llama.cpp and saw a schema cut the amount of thinking roughly in half. A schema and a reasoning phase interact on both engines. On llama.cpp 2.47.0 the JSON still reached content. On MLX 1.11.0 with Qwen3.6 it did not.

What to do when LM Studio structured output comes back empty

There are three ways round it, and our run covers the first two.

The first is to ask for the data through a tool call. The Qwen3.6 MLX build that failed every schema request passed all eleven of our tool-calling checks in the same session, and each call’s arguments arrived as valid JSON in the tool_calls field. Define one function whose parameters are your schema and tell the model to call it. Coding agents already work this way, which is why local tool calling in Hermes and OpenCode is unaffected by this fault.

The second is to run the GGUF build for work that needs a schema. On llama.cpp 2.47.0 the Q4_K_M file returned all three objects in content. It gives up the speed we measured in our MLX vs llama.cpp comparison for Qwen3.6, where MLX read a long prompt about 2.5 times faster, so the choice depends on which matters more for the job.

The third, which we did not test beyond reading our own responses, is a fallback in the client: when content is empty and the finish reason is stop, parse reasoning_content. In our five failed responses that field held the complete object and nothing else.

msg = response["choices"][0]["message"]
text = msg.get("content") or msg.get("reasoning_content") or ""
data = json.loads(text)

Treat that as a patch with a short life. It depends on the reasoning field holding only the JSON, which was true here and is not promised anywhere, and it will hide the fault from you on the day the engine is fixed or changes again.

The plain test above takes under a minute per model. Any pipeline that reads message.content after a schema request is worth that test whenever the engine or the build changes, because the failure is silent: the status is 200, the finish reason is stop, and the only symptom is an empty string.

S

Stride El

is an editor at StrideNote.net: writes most of the notes, keeps the site running, and replies to email.