The right max_tokens for one task on one local model turned out to be about 4,300. Every guess below that, 16, 256, 1,024 and 4,096, returned an empty string, and the last of those failed four times at seven minutes each before anyone thought to stop guessing and measure.
Setting max_tokens for a reasoning model is a measurement, not a setting. Here is the procedure that came out of that afternoon on a studio test machine, and the numbers behind it.
Why max_tokens for a reasoning model is not the answer length
On a plain model, max_tokens bounds the reply. On a reasoning model it bounds the reply plus everything the model thinks before the reply, and the thinking comes first. OpenRouter’s documentation puts it in one line, “Reasoning tokens are counted as output tokens for billing purposes”, and adds the constraint that follows: max_tokens “must be strictly higher than the reasoning budget to ensure there are tokens available for the final response after thinking.”
When the budget runs out mid thought, the visible answer is empty, finish_reason is length, and the reasoning count in usage.completion_tokens_details equals the cap minus a few tokens. Every API puts that count somewhere different, but every one of them puts it somewhere. That is what four runs at a 4,096 cap looked like on gemma-4-31b-qat through LM Studio, and it is a known shape of failure elsewhere; a bug report on Google’s Python SDK describes the same signature for a different model.
The mistake is treating the cap as a property of the answer, which is one word here, instead of a property of the task, which on a 64 by 64 board was 4,274 tokens of thought.
How to find what a task costs before you set max_tokens
Run the real input once with a cap you would never use in production. On this machine that was 16,384, and the answer arrived after 4,274 reasoning tokens with finish_reason: stop. That number is the cost of the task, and nothing short of running it will tell you what it is.
The cost is not fixed across inputs. On the same model and instruction, the reasoning spent scaled with the size of the board rather than the length of the prompt:
| Input | Prompt tokens | Reasoning tokens |
|---|---|---|
| 8 by 8 grid | 147 | 692 |
| 16 by 16 grid | 343 | 2,136 |
| 32 by 32 grid | 1,127 | 2,673 |
| 64 by 64 grid, compressed | 1,522 | 4,274 |
| 64 by 64 grid, raw | 4,179 | 4,274 |
So measure with the largest input the task will see, not a typical one. A cap tuned on a small board is the cap that fails on the big one, and the failure looks identical to a broken model.
How much headroom to leave above the measured reasoning
Three identical calls, same prompt, same temperature, same model, thought for 2,136, 1,762 and 8,076 tokens. The third took four and a half times as long as the second and chose a different action. That is the run to run spread on this stack, and it is why one measurement is not enough: the run you measured was not the longest one the model will produce, and the longest one may be several times longer.
Our rule, from the measurements rather than from theory: run the largest input several times, take the largest reasoning count you saw, and set max_tokens well above it, at least double, rounded up to a power of two. For the 16 by 16 board that is above 8,076, so 16,384. The cost of headroom is nothing when the model stops early, because it stops early. The cost of too little is a seven minute empty reply, and the spread above says a cap that looked generous on Monday will be short on Tuesday.
Do not trust the parameter that sounds like it manages this for you. reasoning_effort set to low on this model produced 4,096 reasoning tokens, double the unconstrained run, and high produced 1,556, which is the inverse of what the name suggests. Whatever it does, it is not a budget you can plan around.
What the cap minus three pattern means
At a cap of 16, reasoning was 13. At 1,024 it was 1,021. At 4,096 it was 4,093. Every truncated run reported thinking equal to the cap minus three, and the pattern is convincing enough to lead a careful person to the wrong conclusion, that the model never stops and no cap will ever be enough.
It means the opposite of that. A truncated run reports the cap because it was cut off at the cap; the number describes your setting, not the model. The only observation that tells you anything about the task is a run that terminated, with finish_reason: stop, and a run that terminated is the one thing a too small cap can never give you. That is the whole reason to measure once with a cap you would never ship.
The three tokens are the visible part: the model was cut off while thinking and the server closed the response with a handful of tokens outside the reasoning block. The count is an artefact of where the cut fell.
When shrinking the input beats raising max_tokens
Raising the cap works, and on the full board it cost 442.7 seconds per answer. The same board downsampled to 16 by 16 answered in 191.9 seconds at 2,136 tokens of thought, and summarised to nine lines of colour counts it answered in 89.2 seconds at 990. The same information, three costs.
So the cap is the second decision, not the first. Decide what the model needs to see, measure what that costs, then set the cap above it with headroom. If the measured cost is more than you can afford per call, the fix is on the input side; a bigger cap only makes an unaffordable call succeed slowly.
The short version, for the file you keep next to the model config. Measure several times at 16,384 on the largest input. Read reasoning_tokens on the runs that say stop and keep the largest. Set max_tokens to at least double that, rounded up. If that number makes the call too slow, shrink what the model has to look at and measure again. Never set the cap from the length of the answer you want, because on a reasoning model the answer is the last and smallest thing it writes.
