BerriAI/litellm · error · ValueError
tools or tool_choice cannot be set if using text
Error message
tools or tool_choice cannot be set if using text
What it means
Token counting for tools/tool_choice is only implemented on the messages= path, which mirrors OpenAI's function-definition overhead accounting. When counting a bare text= string there is no place for tool definitions to contribute, so passing tools or tool_choice together with text= is rejected.
Source
Thrown at litellm/litellm_core_utils/token_counter.py:381
#########################################################
# Flag to disable token counter
# We've gotten reports of this consuming CPU cycles,
# exposing this flag to allow users to disable
# it to confirm if this is indeed the issue
#########################################################
if litellm.disable_token_counter is True:
return 0
verbose_logger.debug("messages in token_counter: %s, text in token_counter: %s", messages, text)
if text is not None and messages is not None:
raise ValueError("text and messages cannot both be set")
if use_default_image_token_count is None:
use_default_image_token_count = False
if text is not None:
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")
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Wrap the string as messages: token_counter(model=m, messages=[{"role": "user", "content": text}], tools=tools, tool_choice=tc).
- If only the raw string count is needed, drop tools/tool_choice from the call.
Example fix
# before
n = litellm.token_counter(model="gpt-4o", text=prompt, tools=my_tools, tool_choice="auto")
# after
n = litellm.token_counter(model="gpt-4o",
messages=[{"role": "user", "content": prompt}], tools=my_tools, tool_choice="auto") Defensive patterns
Strategy: validation
Validate before calling
if tools or tool_choice:
assert messages is not None, "tools counting requires messages=, not text="
n = litellm.token_counter(model=m, messages=messages, tools=tools, tool_choice=tool_choice)
else:
n = litellm.token_counter(model=m, text=text, messages=messages) Prevention
- Treat tools/tool_choice as companions to messages only.
- Centralize token estimation in one helper that enforces the pairing rules.
When it happens
Trigger: litellm.token_counter(text="...", tools=[...]) or token_counter(text="...", tool_choice="auto") - the caller wants a prompt-plus-tools estimate but passed the prompt as text= instead of as messages.
Common situations: Cost estimators built around a prompt template string plus a tool list; migrating from counting only the user string to accounting for tool definitions.
Related errors
- text and messages cannot both be set
- Invalid detail value: {detail}. Expected 'low', 'high', or '
- 'models' param not in kwargs
- start_time_utc and end_time_utc must be provided together
- start_time_utc is required for getting a payload from GCS Bu
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/4991ac4376d006f7.
Report an issue: GitHub.