BerriAI/litellm · error · ValueError
Either text or messages must be provided
Error message
Either text or messages must be provided
What it means
token_counter() received neither text= nor messages=, so there is nothing to count. This fires when both arguments are None (including explicitly passing None), and litellm.disable_token_counter is not set. It is a fail-fast guard against silently returning 0 for a no-op call.
Source
Thrown at litellm/litellm_core_utils/token_counter.py:398
if tools or tool_choice:
raise ValueError("tools or tool_choice cannot be set if using text")
if isinstance(text, list):
text_to_count = "".join(t for t in text if isinstance(t, str))
elif isinstance(text, str):
text_to_count = text
count_function: Final = _get_count_function(model, custom_tokenizer)
num_tokens = count_function(text_to_count)
elif messages is not None:
new_messages: Final = cast(list[AllMessageValues], convert_list_message_to_dict(messages))
params: Final = _MessageCountParams(model, custom_tokenizer)
num_tokens = _count_messages(params, new_messages, use_default_image_token_count, default_token_count)
if count_response_tokens is False:
includes_system_message: Final = any([message.get("role", None) == "system" for message in new_messages])
num_tokens += _count_extra(params.count_function, tools, tool_choice, includes_system_message)
else:
raise ValueError("Either text or messages must be provided")
return num_tokens
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`.
"""View on GitHub (pinned to 6c2dcb801b)
Solutions
- Check the argument before calling: only invoke when text or messages is truthy.
- Fix the upstream variable that is unexpectedly None (log it) rather than defaulting blindly.
- If empty inputs are expected, guard at the call site: return 0 when both are absent.
Example fix
# before
n = litellm.token_counter(model=m, text=msg.get("content")) # content is None -> raises
# after
content = msg.get("content")
n = litellm.token_counter(model=m, text=content) if isinstance(content, str) else 0 Defensive patterns
Strategy: type-guard
Validate before calling
if not text and not messages:
return 0 # nothing to count
return litellm.token_counter(model=model, text=text, messages=messages) Type guard
def has_countable_payload(text, messages) -> bool:
return bool(text) or bool(messages) Prevention
- Log inputs to token counting in debug builds to catch silent None payloads.
- Guard optional fields (message.get('content')) before counting.
When it happens
Trigger: token_counter(model="gpt-4o") or token_counter(model=m, text=None, messages=None) - typically a wrapper whose payload computation failed on both branches, or variables that are None due to upstream bugs.
Common situations: Calling token_counter on optional fields (e.g. message.get("content")) that are None; refactors renaming the messages variable; counting an assistant reply that has no content.
Related errors
- text and messages cannot both be set
- tools or tool_choice cannot be set if using text
- Unsupported type {type(value)} for key tool_calls in message
- Unsupported tool call {tool_call} must contain a function ke
- Unsupported type {type(value)} for key function_call in mess
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/11cd5fc283a7326a.
Report an issue: GitHub.