BerriAI/litellm · error · AnthropicError
max_tokens is required for Anthropic /v1/messages API
Error message
max_tokens is required for Anthropic /v1/messages API
What it means
In the Anthropic-to-Anthropic transformation (request already in /v1/messages spec, no translation needed), the handler pops 'max_tokens' from the optional params and rejects requests where it is absent. Unlike OpenAI chat completions, the Anthropic Messages API makes max_tokens mandatory, and litellm enforces that at transformation time with a 400 AnthropicError.
Source
Thrown at litellm/llms/anthropic/experimental_pass_through/messages/transformation.py:495
optional_params.pop("temperature", None)
def transform_anthropic_messages_request(
self,
model: str,
messages: list[dict],
anthropic_messages_optional_request_params: dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> dict:
"""
No transformation is needed for Anthropic messages
This takes in a request in the Anthropic /v1/messages API spec -> transforms it to /v1/messages API spec (i.e) no transformation is needed
"""
max_tokens: Final = anthropic_messages_optional_request_params.pop("max_tokens", None)
if max_tokens is None:
raise AnthropicError(
message="max_tokens is required for Anthropic /v1/messages API",
status_code=400,
)
self._translate_reasoning_effort_to_anthropic(
model=model,
optional_params=anthropic_messages_optional_request_params,
custom_llm_provider=self._resolved_provider,
)
self._translate_legacy_thinking_for_adaptive_model(
model=model,
optional_params=anthropic_messages_optional_request_params,
custom_llm_provider=self._resolved_provider,
)
self._translate_adaptive_effort_for_non_adaptive_model(
model=model,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Add max_tokens to the request body: e.g. max_tokens=1024 (or 4096+ for reasoning-heavy workloads).
- Set a default in your wrapper so every outbound Anthropic-format request carries it.
- Budget max_tokens above the model's thinking budget if thinking is enabled, or the upstream call will fail next.
Example fix
# before
body = {"model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "hi"}]}
# after
body = {"model": "claude-sonnet-4-5", "messages": [{"role": "user", "content": "hi"}], "max_tokens": 1024} Defensive patterns
Strategy: validation
Validate before calling
def with_max_tokens(body: dict, default_max_tokens: int = 1024) -> dict:
if not body.get("max_tokens"):
body = {**body, "max_tokens": default_max_tokens}
return body Type guard
def has_max_tokens(body: dict) -> bool:
mt = body.get("max_tokens")
return isinstance(mt, int) and not isinstance(mt, bool) and mt > 0 Try / catch
try:
resp = litellm.anthropic_messages(**body)
except Exception as e:
if "max_tokens is required" in str(e):
body["max_tokens"] = 1024
resp = litellm.anthropic_messages(**body)
else:
raise Prevention
- Remember max_tokens is mandatory on the Anthropic Messages API (unlike OpenAI).
- Set a default in your request builder so every Anthropic-format call carries it.
- When thinking is enabled, budget max_tokens above the thinking budget.
When it happens
Trigger: Sending an Anthropic-format request body without a top-level 'max_tokens' field (None after pop). Typical when porting OpenAI-style calls (where max_tokens is optional) to the anthropic_messages endpoint without adding it.
Common situations: Migrating from litellm.completion (OpenAI params) to anthropic_messages and dropping max_tokens; assuming the gateway injects a default; streaming clients that build minimal bodies.
Related errors
- WebSearchInterception: missing follow-up messages
- Invalid first message. Should always start with 'role'='user
- Unable to parse anthropic tool result for message: {message}
- Unable to parse anthropic file message: {message}
- Either file_data or file_id must be present in the file mess
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/3b2069721e150324.
Report an issue: GitHub.