BerriAI/litellm · error · ValueError
Message must be a dictionary
Error message
Message {i} must be a dictionary What it means
Raised while iterating a Converse-format messages list: element i is not a dict. Each message must be an object with role/content keys; a non-dict element (string, number, nested list) fails per-index validation and the message includes the offending index.
Solutions
- Find element i named in the error and wrap/convert it into {'role': ..., 'content': ...}
- Parse JSON strings before insertion: json.loads(m) if isinstance(m, str) and m.startswith('{')
- Sanitize the list upstream: messages = [m for m in messages if isinstance(m, dict)] plus explicit conversion of stragglers
Example fix
# before
messages = ['hello', {'role': 'assistant', 'content': 'hi'}]
# after
messages = [
{'role': 'user', 'content': 'hello'},
{'role': 'assistant', 'content': 'hi'},
] Defensive patterns
Strategy: type-guard
Validate before calling
def assert_all_messages_are_dicts(messages: list) -> None:
for i, m in enumerate(messages):
if not isinstance(m, dict):
raise TypeError(f'message {i} is {type(m).__name__}, expected dict: {m!r:.80}') Type guard
def all_messages_dicts(messages: list) -> bool:
return all(isinstance(m, dict) for m in messages) Try / catch
try:
handler.count_tokens(body)
except ValueError as e:
m = re.search(r'Message (\d+) must be a dictionary', str(e))
if m:
i = int(m.group(1))
body['messages'][i] = normalize_to_message(body['messages'][i]) # str -> {'role':'user','content':s}
handler.count_tokens(body)
else:
raise Prevention
- Convert raw strings to {'role': 'user', 'content': s} at ingestion
- Run a sanitize step: parse JSON-looking strings, wrap non-dicts or drop them with a warning
- Property-test request builders with mixed-shape inputs
When it happens
Trigger: messages=['hello', {...}] (mixed raw strings and message objects); messages=[[{...}]] (extra nesting); messages containing JSON strings that were never parsed: messages=['{"role": "user"}'].
Common situations: Concatenating user-typed input directly into the list; LLM-generated request bodies with inconsistent shapes; pipelines that sometimes emit content-only strings instead of {role, content} dicts.
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 have a 'content' field
- Message must have a 'role' field
- messages must be a list
- messages parameter is required for Converse input
- model parameter is required
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/34736f53a762b65d.
Report an issue: GitHub.
Appendix: source
Thrown at litellm/llms/bedrock/count_tokens/transformation.py:271
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)