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

  1. Check the argument before calling: only invoke when text or messages is truthy.
  2. Fix the upstream variable that is unexpectedly None (log it) rather than defaulting blindly.
  3. 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

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


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/11cd5fc283a7326a. Report an issue: GitHub.