BerriAI/litellm · error · ValueError
messages must be a list
Error message
messages must be a list
What it means
Raised by validate_count_tokens_request when a Converse-format request supplies 'messages' whose Python type is not list. The Converse contract requires an ordered list of message objects; strings, dicts, or None fail this structural check before token counting starts.
Solutions
- Wrap a single message in a list: messages=[msg] instead of messages=msg
- If the content came from JSON, ensure you parsed it (json.loads) rather than passing the serialized string
- Add a unit assertion isinstance(messages, list) at request-build time
Example fix
# before
body = {'model': 'bedrock/...', 'messages': {'role': 'user', 'content': 'hello'}}
# after
body = {'model': 'bedrock/...', 'messages': [{'role': 'user', 'content': 'hello'}]} Defensive patterns
Strategy: type-guard
Validate before calling
def assert_messages_is_list(body: dict) -> None:
if 'messages' in body and not isinstance(body['messages'], list):
raise TypeError(f"'messages' must be a list, got {type(body['messages']).__name__}") Type guard
from typing import TypeGuard
def is_message_list(value: object) -> TypeGuard[list[dict]]:
return isinstance(value, list) and len(value) > 0 and all(isinstance(m, dict) for m in value) Try / catch
try:
handler.count_tokens(body)
except ValueError as e:
if 'messages must be a list' in str(e):
single = body['messages']
body['messages'] = [single] if isinstance(single, dict) else list(single)
handler.count_tokens(body)
else:
raise Prevention
- Normalize single dicts into one-element lists at the boundary of your app
- Type request builders with TypedDict/Pydantic so mismatches fail at lint/test time
- Assert list-ness in request-factory unit tests
When it happens
Trigger: Passing messages as a single dict ({'role': 'user', ...}) instead of a one-element list; passing a raw string ('hello'); passing None; double-wrapping like {'messages': {'messages': [...]}}.
Common situations: Porting code from APIs that accept a lone message object; template/ORM layers that collapse single-element lists; copy-paste from chat-completions snippets that build messages differently.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Message must be a dictionary
- messages parameter is required for Converse input
- Message must have a 'content' field
- Message must have a 'role' field
- model parameter is required
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/ed895d2b1800d680.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/llms/bedrock/count_tokens/transformation.py:267
Args:
request_data: The request payload
Raises:
ValueError: If the request is invalid
"""
if not request_data.get("model"):
raise ValueError("model parameter is required")
input_type: Final = self._detect_input_type(request_data)
if input_type == "converse":
# Validate Converse format (messages-based)
messages: Final = request_data.get("messages", [])
if not messages:
raise ValueError("messages parameter is required for Converse input")
if not isinstance(messages, list):
raise ValueError("messages must be a list")
for i, message in enumerate(messages):
if not isinstance(message, dict):
raise ValueError(f"Message {i} must be a dictionary")
if "role" not in message:
raise ValueError(f"Message {i} must have a 'role' field")
if "content" not in message:
raise ValueError(f"Message {i} must have a 'content' field")
else:
# For InvokeModel format, we need at least some content to count tokens
# The content structure varies by model, so we do minimal validation
if len(request_data) <= 1: # Only has 'model' field
raise ValueError("Request must contain content to count tokens")
View on GitHub (pinned to 77b7c6c40c)