finish_reason = 'length' means the runtime hit the max_tokens cap before the model emitted EOS or matched a stop sequence. It is a hard numeric truncation, not a content-driven stop.
Imagine asking someone to write you a story but giving them only 30 seconds. If they finish naturally before the timer runs out, they put a period at the end and hand it to you ('stop'). If the timer rings while they are still writing, they have to stop mid-sentence ('length'). The 'length' signal means the timer ran out, not that the story was finished. To get a complete answer you have to either give them more time, ask for a shorter story, or come back and ask them to keep writing where they left off.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
finish_reason is one of the most under-appreciated fields in LLM APIs. It is small, discrete, and easy to ignore, but it carries the entire signal about why generation ended. Production stacks that ignore it produce silently broken outputs; production stacks that handle it correctly catch a whole class of bugs at the source.
The enum maps to three structurally different termination causes, and 'length' is the cause that requires the most active handling. A 'stop' termination means the response is complete and ready to consume. A 'length' termination means the response was truncated and consumers must decide whether to extend, retry, or fail.
This deep dive walks through the termination cause taxonomy, the operational handling for each case, and the production patterns that make finish_reason a first-class observability signal rather than a footnote in the response payload.
The three termination causes
Decoding can end for exactly three structurally distinct reasons.
Content-driven: the model itself chose to stop. This happens in two sub-cases. The model can sample its EOS (end-of-sequence) token, a special token the training process taught it to emit at natural completion boundaries. Or the caller can supply stop sequences (strings like '\n\nHuman:' or '</response>') that the runtime checks against the decoded suffix after each token, halting on match. Both sub-cases surface as 'stop' in OpenAI's enum; Anthropic distinguishes them as 'end_turn' versus 'stop_sequence'.
Length-driven: a numeric cap on output length was hit before the model chose to stop. The relevant cap is max_tokens in older APIs, or max_completion_tokens / max_output_tokens in newer APIs (the renamed parameter reflects providers' need to be explicit that this counts all generated tokens, including reasoning tokens for reasoning-capable models). This surfaces as 'length'.
Policy-driven: an external policy intervened. Safety filters, content moderation, RLHF-trained refusals, tool-call dispatching, and similar runtime policies can halt generation regardless of what the model wanted to do. These get their own enum values: 'content_filter', 'refusal', 'tool_calls', 'tool_use'.
The key insight is that these three classes correspond to different agents making the stop decision: the model (content-driven), the runtime budget enforcer (length-driven), and an external policy (policy-driven). Each requires different downstream handling. Conflating them is the source of most finish_reason bugs in production code.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
| Cause | OpenAI | Anthropic | Google Gemini |
|---|---|---|---|
| EOS sampled (natural completion) | 'stop' | 'end_turn' | 'STOP' |
| Stop sequence matched | 'stop' | 'stop_sequence' | 'STOP' |
| max_tokens cap hit | 'length' | 'max_tokens' | 'MAX_TOKENS' |
| Safety / content policy | 'content_filter' | 'refusal' or stop_reason variants | 'SAFETY' |
| Tool / function call emitted | 'tool_calls' / 'function_call' | 'tool_use' | 'TOOL_CALL' |
Real products, models, and research that use this idea.
- OpenAI Chat Completions returns finish_reason = 'length' when max_tokens (or max_completion_tokens for o-series) is hit before EOS.
- Anthropic's Messages API uses stop_reason = 'max_tokens' for the same condition; the field name differs but the semantics match.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow do continuation calls work mechanically, and what are the edge cases?
Resubmit the original prompt plus the truncated assistant output as an assistant-prefix in the message list. The model continues from exactly where it stopped, deterministically modulo sampling. Edge cases: mid-token truncation (the cap fell in the middle of a multi-byte token's bytes), mid-tool-call truncation (the JSON is malformed and cannot be parsed), and reasoning-model continuations where the hidden reasoning state is not exposed and resumption is approximate.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Reading finish_reason = 'length' as 'the model finished naturally and the answer is long'. It is the opposite: the answer was cut off because the runtime hit a numeric cap. Treating a 'length' termination as a complete response leads to silently truncated outputs.
60 second bullets to scan on the way to the call.
The three classes of termination causes (content-driven, length-driven, policy-driven)
Why 'length' specifically means max_tokens was hit before any natural stop
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.