Hitting This model's maximum context length is N tokens? The fix is to fit input + requested output under the window:
- Count tokens before you send and trim the largest input — usually conversation history or a giant RAG chunk. Use the token counter.
- Lower
max_tokens— it’s reserved up front and counts against the window, so an oversized value alone can trigger this. - If the input is genuinely large, switch to a bigger-context model or chunk it (map-reduce) instead of sending it all at once.
context_length_exceeded means the total size of your request — input tokens plus the output tokens you reserved with max_tokens — is larger than the model’s context window. Unlike a 429, this isn’t about rate; it’s about a single request being too big. It arrives as an HTTP 400, so retrying it unchanged fails every time. You fix it by making the request smaller.
What this error means
The body names the limit and how far over you went:
{
"error": {
"message": "This model's maximum context length is 128000 tokens. However, your messages resulted in 129500 tokens (124500 in the messages, 5000 in the completion). Please reduce the length of the messages or completion.",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
} The key idea most people miss: the context window is shared between input and output. If a model has a 128K window and you send 126K tokens of input, you can only ask for ~2K output tokens. Set max_tokens to 8K and you’ll overflow even though the input alone fit.
| Model context window | Your input tokens | max_tokens you reserved | Result |
|---|---|---|---|
| 128,000 | 124,500 | 5,000 | ❌ 129,500 > 128,000 |
| 128,000 | 124,500 | 2,000 | ✅ fits |
| 128,000 | 60,000 | 16,000 | ✅ fits |
Common causes
- Runaway conversation history. A chat app that appends every turn grows unbounded; eventually the full transcript blows the window. The single most common cause in production.
- Oversized RAG context. Retrieving too many or too-large chunks and stuffing them all into the prompt.
- Large documents pasted whole. Summarizing or analyzing a long PDF/codebase in one call.
max_tokensset too high. The completion reservation is counted against the window before generation starts. A big default (e.g. 16K) eats space the input needed.- Wrong model assumption. Targeting a model with a smaller window than you thought. Check the real number with the context-window checker.
How to fix it
Count tokens before you send
Stop guessing. Measure input size and leave headroom for the output:
import tiktoken
def num_tokens(messages, model="gpt-4o-mini"):
enc = tiktoken.encoding_for_model(model)
# ~4 tokens of overhead per message for role/formatting metadata.
total = sum(len(enc.encode(m["content"])) + 4 for m in messages)
return total + 2 # priming tokens
CONTEXT_WINDOW = 128_000
RESERVE_FOR_OUTPUT = 2_000
msgs = [{"role": "user", "content": "..."}]
budget = CONTEXT_WINDOW - RESERVE_FOR_OUTPUT
if num_tokens(msgs) > budget:
raise ValueError("Input too large — trim history or chunk the document.") import { encode } from "gpt-tokenizer";
function numTokens(messages) {
return messages.reduce((n, m) => n + encode(m.content).length + 4, 0) + 2;
}
const CONTEXT_WINDOW = 128_000;
const RESERVE_FOR_OUTPUT = 2_000;
const msgs = [{ role: "user", content: "..." }];
if (numTokens(msgs) > CONTEXT_WINDOW - RESERVE_FOR_OUTPUT) {
throw new Error("Input too large — trim history or chunk the document.");
} Trim conversation history with a sliding window
For chat apps, keep the system prompt plus the most recent turns that fit, and drop the oldest:
import tiktoken
def fit_history(messages, model="gpt-4o-mini", budget=120_000):
enc = tiktoken.encoding_for_model(model)
size = lambda m: len(enc.encode(m["content"])) + 4
system = [m for m in messages if m["role"] == "system"]
rest = [m for m in messages if m["role"] != "system"]
used = sum(size(m) for m in system)
kept = []
for m in reversed(rest): # newest first
if used + size(m) > budget:
break
kept.append(m); used += size(m)
return system + list(reversed(kept)) When the input is genuinely large: summarize or chunk
- Summarize older turns. Periodically replace the oldest history with a short model-generated summary so context stays bounded.
- Chunk + map-reduce. Split a long document, process each chunk, then combine the partial results in a final call. This is the right pattern for “analyze this whole book/repo.”
- Retrieve less. In RAG, lower your top-k or shrink chunk size so only the most relevant context goes in.
Or switch to a larger-context model
If you legitimately need more room, move to a model with a bigger window. Confirm the exact size first with the context-window checker — windows differ widely across models and vendors.
How to prevent it
- Always budget the window as input + output, reserving space for
max_tokensbefore you build the prompt. - Cap and prune history on every turn rather than waiting for the overflow.
- Set
max_tokensto what you actually need, not a large default. - Pre-flight large requests through a token count in your code path; fail with a clear message instead of a raw 400.
Embeddings and other endpoints have their own per-input limits separate from chat. A 400 about input length on /embeddings is the same class of problem — split the input into smaller pieces.
Related
- The other big OpenAI 400/429 errors: 429 rate limit and insufficient_quota.
- Size requests before sending with the token counter and check model limits with the context-window checker.
If your workload keeps outgrowing one model’s window, routing to a larger-context model on demand is simplest behind a gateway. OpenRouter exposes many models (and their context sizes) through one OpenAI-compatible API, so you can pick a larger window per request. (Affiliate link.)
What to do next
- Count input tokens in your request path and reserve room for the output.
- Prune or summarize history so chat sessions stay under the window.
- Chunk large documents with map-reduce instead of one giant prompt.
- Verify the model’s real window and switch up a tier if you truly need more.