BerriAI/litellm · error · ValueError
Unsupported type {type(value)} for key tool_calls in message
Error message
Unsupported type {type(value)} for key tool_calls in message {message} What it means
While counting an assistant message's tool_calls payload, the value under the 'tool_calls' key is not a list. The OpenAI schema requires tool_calls to be a list of {id, type, function} objects; anything else (dict, string, None) cannot be iterated, so the counter raises with the offending type and the full message echoed.
Source
Thrown at litellm/litellm_core_utils/token_counter.py:419
def _count_function_call_tokens(
key: str,
value: Any,
message: Mapping[str, Any],
count_function: TokenCounterFunction,
) -> int:
"""
Count tokens contributed by an assistant message's tool/function call payload.
Handles both the modern `tool_calls` list and the legacy OpenAI
`function_call` dict. Only the `arguments` string is counted (matching the
existing tool_calls behavior); names are accounted for elsewhere via the
tool/function definitions and `tool_choice`.
"""
if key == "tool_calls":
if not isinstance(value, list):
raise ValueError(f"Unsupported type {type(value)} for key tool_calls in message {message}")
total = 0
for tool_call in value:
if "function" not in tool_call:
raise ValueError(f"Unsupported tool call {tool_call} must contain a function key")
function_arguments = tool_call["function"].get("arguments", "")
total += count_function(str(function_arguments))
return total
if key == "function_call":
if not isinstance(value, Mapping):
raise ValueError(f"Unsupported type {type(value)} for key function_call in message {message}")
return count_function(str(value.get("arguments", "")))
raise ValueError(f"Unexpected key {key!r}; expected 'tool_calls' or 'function_call'")
def _count_messages(
params: _MessageCountParams,
messages: list[AllMessageValues],
use_default_image_token_count: bool,View on GitHub (pinned to 6c2dcb801b)
Solutions
- Normalize to a list: always a list of tool-call objects (wrap a lone dict, drop None).
- Fix the producer: validate tool_calls against the OpenAI schema before persisting messages.
Example fix
# before
msg = {"role": "assistant", "tool_calls": {"id": "1", "function": {"name": "get_weather", "arguments": "{}"}}}
# after
msg = {"role": "assistant", "tool_calls": [{"id": "1", "type": "function", "function": {"name": "get_weather", "arguments": "{}"}}]} Defensive patterns
Strategy: type-guard
Type guard
def is_valid_tool_calls(value) -> bool:
return value is None or (isinstance(value, list) and all(
isinstance(tc, dict) and "function" in tc for tc in value)) Try / catch
try:
n = litellm.token_counter(model=m, messages=msgs)
except ValueError as e:
if "tool_calls" in str(e):
msgs = normalize_tool_calls(msgs) # wrap dict->list, drop None
n = litellm.token_counter(model=m, messages=msgs)
else:
raise Prevention
- Validate assistant tool_calls against the OpenAI schema before persisting to history.
- Wrap lone tool-call dicts in a list at the point of construction.
When it happens
Trigger: An assistant message with tool_calls as a dict instead of a list (e.g. {"role":"assistant","tool_calls":{"function":...}}), tool_calls=null, or a message where tool_calls was serialized to a JSON string before reaching token_counter or cost calculation.
Common situations: Hand-constructed agent conversation histories; messages round-tripped through a queue/DB that mutated the shape; third-party frameworks emitting a single tool-call object instead of a list; None tool_calls on the final assistant message.
Related errors
- Unsupported tool call {tool_call} must contain a function ke
- Unknown Anthropic content type: '{content_type}'
- Invalid template message type: {type(template_message)}
- WebSearchInterception: missing follow-up messages
- text and messages cannot both be set
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/eb3c8a8ae3bdca2f.
Report an issue: GitHub.