A local model returned nothing four times in a row on a studio test machine, seven minutes a time. The first useful thing anyone did about it was ask it to reply with the word ACTION1. It did, in 2.8 seconds.
That one call ruled out the server, the model file, the chat template and the client in under three seconds, which is faster than reading any of their logs. The rest of the diagnosis was three more calls. Here is the ladder we now use to debug a local model, why each rung is there, and what to do at the rung where it breaks.
Why the first call to a local model should be trivial
When a local model misbehaves, the candidates are the server, the model, the template, the parameters, the prompt, and the input. Debugging any of them from the failing call is guesswork, because the failing call exercises all six at once.
A trivial call exercises the first four and none of the last two. If "Reply with exactly: ACTION1" comes back as ACTION1, then LM Studio is up, the model loaded, the chat template is producing an answer, and the client is parsing it. Every explanation that involves those is closed in one round trip. On this machine it took 2.8 seconds and 25 tokens of reasoning, and it was the first evidence in an afternoon that the model was fine.
The reflex is to reach for a bigger max_tokens or a different prompt first. Both are changes to the failing call, and neither tells you which of the six candidates you just changed.
How to debug a local model with a four call ladder
Each call adds one variable to the one before it. Run them in order and stop at the first one that fails; that rung is where the problem lives.
1. "Reply with exactly: ACTION1" -> 2.8s, 25 reasoning tokens, "ACTION1"
2. "What is 2+2? Number only." -> 4.5s, 44 reasoning tokens, "4"
3. An 8x8 grid, "pick one action" -> 25.8s, 305 reasoning tokens, "ACTION1"
4. The real 64x64 grid, same instruction -> 407.8s at a 4,096 cap, empty
442.7s at a 16,384 cap, "ACTION1"Rung one proves the stack. Rung two proves the model will do a small amount of thinking and still stop; the reasoning count went from 25 to 44 and the answer was one character. Rung three proves it can read a board at all: a toy grid, the same instruction the real task uses, and a legal answer after 305 tokens of thought. Rung four is the real input, and on its first run it failed.
Because the first three passed, the failure on the fourth could only be about the input or the parameters around it. Raising the cap from 4,096 to 16,384 produced an answer at 4,274 reasoning tokens. The model had wanted 4,274, been given 4,096, and been cut off 178 tokens short, four times. That is the whole story of the empty reply, and the ladder found it in five calls where the failing call alone had produced only theories.
The numbers are one model, gemma-4-31b-qat, on one Mac. The shape is what transfers: cost climbs with the size of the input, and it climbs by an amount you cannot see until you let the model finish.
How to tell a slow local model from a stuck one
The ladder gives you a clean test. A slow model passes every rung and takes longer at each. A stuck model fails a rung outright: no answer, a wrong shape, a refusal, a timeout. The two look identical from the failing call, because a truncated response and a hung one both arrive as nothing.
Two fields in the response separate them, and the ladder teaches you to read them before the real input arrives. finish_reason is stop when the model chose to end and length when the server cut it off. usage.completion_tokens_details.reasoning_tokens is how much of the output was thinking, and OpenRouter’s reasoning documentation is the plainest statement of why that matters: “Reasoning tokens are counted as output tokens for billing purposes.” They come out of the same cap as the answer. A slow model shows stop with a large reasoning count. A truncated one shows length with a reasoning count equal to your cap minus a few tokens, whatever the cap is, which is the pattern that misleads people into believing the thinking never converges. A hung one shows nothing, because the request never returned. The truncated case is common enough across providers to have its own bug reports; one on Google’s Python SDK describes the identical signature, a max tokens finish reason with empty text, for a different model behind a different API.
Rung three is where to calibrate. If an 8 by 8 board takes 305 tokens of thought and 26 seconds, a board with sixteen times the cells is not going to take 305 tokens, and a cap that was fine for the toy is the first suspect for the real thing.
What to change when the ladder breaks at a rung
Rung one fails: the model is not answering at all. Check the server is serving the model you think, that the chat template is set, and that the client is reading message.content from the right place. Nothing about your task is implicated yet.
Rung two fails: the model answers a copy instruction but not a question. That is usually a template or a stop token problem, and occasionally a model that has been loaded with a broken quantisation. Still not your task.
Rung three fails: the model cannot read a small board. Now it is about the model, or about how the board is encoded. Try the same grid as a colour summary or as a list of coordinates before concluding the model cannot do it; how the grid is written changes the cost by a factor of five.
Rung four fails: the model can do the task and cannot do it at this size or under these parameters. Raise max_tokens well past anything reasonable, once, and read the reasoning count when it terminates. That number is what the task costs. Set the real cap above it, or shrink the input until the cost is one you can pay.
When to stop debugging the model and fix the input
The ladder ends at the point where every rung passes and the real task is merely expensive. That is not a bug, and no parameter fixes it. On this machine the real board cost 4,274 tokens of thought and seven and a half minutes per move, against a human who needs 22 moves for the level. A working model at that speed is not a working agent.
From there the work moves to the input. A 16 by 16 downsample of the same board cost 2,136 tokens and 192 seconds; a nine line colour summary cost 990 and 89. The model did not change. The question did.
Keep the ladder in a file and run it before every session that involves a new model, a new server build or a new kind of input. Five calls, about ten minutes at the top of the ladder and under a minute at the bottom, and it converts “the model is broken” into one of six specific claims, five of which it has already ruled out.
