OpenAI context_length_exceeded ("maximum context length"): Causes and Fixes
Your request fails with HTTP 400 and a message like "This model's maximum context length is N tokens", or a streamed Responses call ends in response.failed with context_length_exceeded. Here is how the limit is actually computed, how to measure a request before sending it, and four ways to keep long conversations under the line.
Quick answer
context_length_exceeded means the request needs more tokens than the model's context window — and that window covers input, the output you reserve, and reasoning tokens. Nothing is generated; resending the same request fails again. Fix it by measuring the input (POST /v1/responses/input_tokens), trimming or summarizing old turns, shrinking tool outputs, and on the Responses API either setting truncation: "auto" or enabling compaction. Lowering an oversized output cap can also be enough.
Symptoms and What the Error Means
On Chat Completions, the classic body:
{
"error": {
"message": "This model's maximum context length is 128000 tokens. However, you requested 1000024 tokens (24 in the messages, 1000000 in the completion). Please reduce the length of the messages or completion.",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
}An older wording, still common in logs, counts only the messages:
This model's maximum context length is 4097 tokens. However, your messages resulted in 4123 tokens. Please reduce the length of the messages.On a streamed Responses API request, the failure arrives as an event rather than an HTTP error body:
{
"type": "response.failed",
"response": {
"id": "resp_5c66...",
"object": "response",
"status": "failed",
"error": {
"code": "context_length_exceeded",
"message": "Your input exceeds the context window of this model. Please adjust your input and try again."
}
}
}What to take from these:
- Match on
code, not message text. The wording differs between endpoints and over time;context_length_exceededis the stable signal. OpenAI's own Codex client detects it by code insideresponse.failed. - The first message splits the count. "24 in the messages, 1000000 in the completion" shows that the output budget you request is part of the check.
- Streaming code needs its own handler. A streamed request can return HTTP 200 and then fail in-stream, so check for
response.failedevents, not just the status code.
This is different from a reply that stops early because it hit your output cap — that is status: "incomplete" with incomplete_details.reason: "max_output_tokens", covered in Unsupported parameter: max_tokens.
Why It Happens
OpenAI's conversation-state guide defines the context window as the maximum tokens usable in a single request, covering input, output and, for some models, reasoning tokens. Requests usually blow past it for one of these reasons:
- History grows without a cap. Every chat turn is resent; after enough turns the transcript alone exceeds the window.
- Tool outputs pile up. In agent loops, file contents, search results and command output are usually the bulk of the tokens, not the conversation.
- The output budget is too large. Setting
max_completion_tokensormax_output_tokensto the model's maximum leaves less room for input. - Hidden inputs. Tool definitions, JSON schemas, images and files all count, and are easy to forget when estimating.
- The input limit is lower than the window. OpenAI's model pages for
gpt-6.1-sol,gpt-6-astra,gpt-6-sol,gpt-6-lunaandgpt-5.6-sollist a 1,050,000-token context window, a maximum input of 922,000 tokens and up to 128,000 output tokens. Input over 922,000 fails even if you request very little output. - Chaining doesn't shrink anything. With
previous_response_id, all earlier turns are still part of the model's input (and billed as input tokens), so a long chain hits the same wall. See previous_response_not_found for the stateless alternative.
Fix 1: Measure Before You Send
Guessing with "characters ÷ 4" is unreliable for code, non-English text, images and tools. OpenAI provides an input-token counting endpoint that takes the same shape as a Responses request:
from openai import OpenAI
client = OpenAI() # official OpenAI endpoint
count = client.responses.input_tokens.count(
model="gpt-6-sol",
input=history, # the exact input you are about to send
tools=tools, # tool definitions count too
)
print(count.input_tokens)curl https://api.openai.com/v1/responses/input_tokens \
-H "Authorization: Bearer sk-YOUR_OPENAI_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-6-sol", "input": "How many tokens is this?"}'Not every OpenAI-compatible provider exposes this endpoint. Where it isn't available, use the usage.input_tokens (Responses) or usage.prompt_tokens (Chat Completions) value from your previous call as a running measurement — it is the exact count for what you sent last time. OpenAI's token-counting guide notes that local tokenizers such as tiktoken only cover plain text, and miss role formatting, tools and images.
Fix 2: Trim History to a Token Budget
The most portable fix is to own the window yourself: keep the system prompt, keep the most recent turns, and drop (or summarize) the rest once a budget is reached.
from openai import OpenAI
client = OpenAI(api_key="sk-YOUR_TOKEN", base_url="https://byesu.com/v1")
INPUT_BUDGET = 200_000 # well under the model's max input
OUTPUT_CAP = 32_000 # leaves room for reasoning + answer
def estimate(msg) -> int:
# Rough guard only; correct it with real usage numbers below
return len(str(msg["content"])) // 3 + 8
def fit(system, turns, budget=INPUT_BUDGET):
kept, used = [], estimate(system)
for msg in reversed(turns): # newest first
cost = estimate(msg)
if used + cost > budget:
break
kept.append(msg)
used += cost
return [system] + list(reversed(kept))
system = {"role": "system", "content": "You are a concise code reviewer."}
turns = [] # your accumulated user/assistant messages
turns.append({"role": "user", "content": "Review the diff below ..."})
resp = client.chat.completions.create(
model="gpt-6-luna",
messages=fit(system, turns),
max_completion_tokens=OUTPUT_CAP,
)
turns.append({"role": "assistant", "content": resp.choices[0].message.content})
print("real input tokens:", resp.usage.prompt_tokens)Two habits make this work in production:
- Truncate tool output at the source. Cap file reads and command output (head plus tail, or a summary) before they enter history. This usually saves more than trimming chat turns.
- Summarize instead of dropping when old turns still matter: replace the oldest block with a short model-written summary message.
Fix 3: Let the Responses API Truncate (truncation: "auto")
The Responses API has a truncation parameter. Its default, "disabled", makes an oversized request fail with a 400 error. Set it to "auto" and the API drops items from the beginning of the conversation until the input fits:
resp = client.responses.create(
model="gpt-6-sol",
input=history,
truncation="auto",
max_output_tokens=32_000,
)This removes the error, but the trade-off is real: the oldest items are dropped without notice. If your first items carry critical rules or the task definition, they are the first to go — re-send that material in a recent turn, or prefer Fix 2 or Fix 4 where you control what survives.
Fix 4: Use Compaction for Long-Running Sessions
For agents and long chats, OpenAI's Responses API offers compaction: the server condenses prior turns into an encrypted compaction item that carries state and reasoning forward in fewer tokens.
Server-side, per request, triggered when the rendered token count crosses your threshold:
resp = client.responses.create(
model="gpt-6.1-sol",
input=history,
store=False,
context_management=[{"type": "compaction", "compact_threshold": 400_000}],
)
history += resp.output # includes the compaction item when one is producedPer OpenAI's compaction guide, once a compaction item appears you can drop the items that precede it from your next request. If you chain with previous_response_id instead, don't prune manually. There is also a standalone /responses/compact endpoint: you send a full window, it returns a compacted window that you pass into the next /responses call as-is (the window you send must itself fit the model's context). Both modes work with store=false.
Compaction is an OpenAI Responses feature; on other providers, confirm it is supported before relying on it, and keep Fix 2 as the fallback.
Size the Output Budget Deliberately
Because the output cap counts toward the check, setting it to the model maximum "just in case" is a common cause of this error on long inputs. OpenAI's reasoning guide suggests reserving at least 25,000 tokens for reasoning and output when you start experimenting; size it from there based on the answers you actually get, rather than to the maximum.
If You're Calling GPT Through byesu
byesu is an AI API gateway with an OpenAI-compatible endpoint at https://byesu.com/v1, supporting both Chat Completions and Responses, billed pay-as-you-go with no subscription.
- Context limits come from the model. The window you get is the model's own limit, so the budgeting and trimming above apply as written; on pay-as-you-go billing, trimming history also cuts the input tokens you pay for on every turn.
- Use
usageas your meter. Readusage.input_tokens(Responses) orusage.prompt_tokens(Chat Completions) from each response and feed it into your trimming logic — it works the same on any endpoint.
To get set up, follow the Quick Start.
FAQ
What does context_length_exceeded mean?
The request needs more tokens than the model's context window allows. The window covers input, output and, for reasoning models, reasoning tokens. On Chat Completions it comes back as a 400 invalid_request_error; on a streamed Responses request it arrives as a response.failed event with error code context_length_exceeded.
Does max_completion_tokens or max_output_tokens count toward the context limit?
It can. One documented form of the error reports the tokens in the messages and the tokens requested for the completion separately, so a very large output cap can push a request over the limit. Current GPT-6 model pages also list a maximum input size below the full context window.
How do I count tokens before sending a request?
OpenAI provides POST /v1/responses/input_tokens, called as client.responses.input_tokens.count in the Python SDK, which returns the exact input token count for a Responses request including tools and images. Local tokenizers like tiktoken only approximate plain text.
What does truncation auto do in the Responses API?
By default truncation is disabled, and a request whose input exceeds the context window fails with a 400 error. With truncation set to auto, the API drops items from the beginning of the conversation until the input fits. It prevents the error but silently removes the oldest context.
What is compaction in the Responses API?
Compaction shrinks a long conversation into an encrypted compaction item that carries prior state forward in fewer tokens. You can enable it per request with context_management and a compact_threshold, or call the standalone /responses/compact endpoint. Both work with store set to false.
