BerriAI/litellm · warning · ValueError
Invalid content item type: {content_type}. Expected str or d
Error message
Invalid content item type: {content_type}. Expected str or dict with 'type' field (text, image_url, tool_use, tool_result, thinking, tool_reference). What it means
A message content list contained an item that is neither a str nor a dict with one of the recognized 'type' discriminators (text, image_url, tool_use, tool_result, thinking, tool_reference). The actual type (or the dict's 'type' value) is reported. When default_token_count is set this is suppressed and the fallback value used instead.
Source
Thrown at litellm/litellm_core_utils/token_counter.py:730
# Claude extended thinking content block
# Count the thinking text and skip signature (opaque signature blob)
thinking_text = str(c.get("thinking", ""))
if thinking_text:
num_tokens += count_function(thinking_text)
elif c["type"] == "tool_reference":
# Anthropic tool-search reference block: a lightweight pointer to
# a deferred tool, e.g. {"type": "tool_reference", "tool_name": ...}.
# The full tool definition is counted via the `tools` param, so we
# only count the referenced name here. Without this branch,
# token_counter raises on tool-search traffic; on the streaming
# anthropic_messages path that nulls response_cost and causes the
# proxy to drop the SpendLogs row entirely (silent cost undercount).
tool_name = str(c.get("tool_name") or "")
if tool_name:
num_tokens += count_function(tool_name)
else:
content_type = c.get("type", type(c).__name__) if isinstance(c, dict) else type(c).__name__
raise ValueError(
f"Invalid content item type: {content_type}. "
f"Expected str or dict with 'type' field (text, image_url, tool_use, tool_result, thinking, tool_reference)."
)
return num_tokens
except Exception as e:
if default_token_count is not None:
return default_token_count
raise ValueError(
f"Error getting number of tokens from content list: {e}, default_token_count={default_token_count}"
)
def _format_function_definitions(tools):
"""Formats tool definitions in the format that OpenAI appears to use.
Based on https://github.com/forestwanglin/openai-java/blob/main/jtokkit/src/main/java/xyz/felh/openai/jtokkit/utils/TikTokenUtils.java
"""
lines: Final = []
lines.append("namespace functions {")View on GitHub (pinned to 6c2dcb801b)
Solutions
- Update litellm to a version that supports the block type (support for new block types lands quickly).
- Filter content lists to known types (or stringify unknown blocks) before counting.
- Pass default_token_count so unknown blocks degrade to an estimate instead of raising.
Example fix
# before
content = [maybe_block for maybe_block in raw if cond] # may contain None
n = litellm.token_counter(model=m, messages=[{"content": content}])
# after
KNOWN = ("text", "image_url", "tool_use", "tool_result", "thinking", "tool_reference")
content = [c for c in content if isinstance(c, str) or (isinstance(c, dict) and c.get("type") in KNOWN)]
n = litellm.token_counter(model=m, messages=[{"content": content}], default_token_count=0) Defensive patterns
Strategy: fallback
Validate before calling
KNOWN = {"text", "image_url", "tool_use", "tool_result", "thinking", "tool_reference"}
content = [c for c in content
if isinstance(c, str) or (isinstance(c, dict) and c.get("type") in KNOWN)] Type guard
def is_countable_content_item(item) -> bool:
if isinstance(item, str):
return True
return isinstance(item, dict) and item.get("type") in {
"text", "image_url", "tool_use", "tool_result", "thinking", "tool_reference"} Try / catch
try:
n = litellm.token_counter(model=m, messages=msgs)
except ValueError:
n = litellm.token_counter(model=m, messages=msgs, default_token_count=0) Prevention
- Upgrade litellm promptly when providers ship new content block types.
- Strip None items from comprehension-built content lists.
- Use default_token_count for untrusted traffic so unknown blocks degrade to estimates.
When it happens
Trigger: Content lists containing None (an optional block that was never built), integers/floats, or dicts with novel 'type' values from newer provider features (e.g. 'document', 'server_tool_use') not yet handled by this litellm version.
Common situations: New Anthropic/OpenAI content block types shipped before litellm support lands; list comprehensions yielding None for skipped items; content assembled from mixed sources.
Related errors
- text and messages cannot both be set
- tools or tool_choice cannot be set if using text
- Either text or messages must be provided
- Unsupported type {type(value)} for key tool_calls in message
- Unsupported tool call {tool_call} must contain a function ke
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/243cabdba456baa8.
Report an issue: GitHub.