BerriAI/litellm · error · ValueError
Either a chat completion object or the text response needs t
Error message
Either a chat completion object or the text response needs to be passed in. Learn more - https://docs.litellm.ai/docs/budget_manager
What it means
ValueError from BudgetManager.update_cost: cost could not be computed because neither the inputs needed for token-based costing (text/messages with a model) nor a chat completion object was supplied. The method computes cost either via litellm.completion_cost(completion_response=...) when a completion object is given, or via token counting when raw text/messages plus model are given; with neither path satisfiable it raises.
Source
Thrown at litellm/budget_manager.py:137
output_text: str | None = None,
):
if model and input_text and output_text:
prompt_tokens = litellm.token_counter(model=model, messages=[{"role": "user", "content": input_text}])
completion_tokens = litellm.token_counter(model=model, messages=[{"role": "user", "content": output_text}])
(
prompt_tokens_cost_usd_dollar,
completion_tokens_cost_usd_dollar,
) = litellm.cost_per_token(
model=model,
prompt_tokens=prompt_tokens,
completion_tokens=completion_tokens,
)
cost = prompt_tokens_cost_usd_dollar + completion_tokens_cost_usd_dollar
elif completion_obj:
cost = litellm.completion_cost(completion_response=completion_obj)
model = completion_obj["model"] # if this throws an error try, model = completion_obj['model']
else:
raise ValueError(
"Either a chat completion object or the text response needs to be passed in. Learn more - https://docs.litellm.ai/docs/budget_manager"
)
self.user_dict[user]["current_cost"] = cost + self.user_dict[user].get("current_cost", 0)
if "model_cost" in self.user_dict[user]:
self.user_dict[user]["model_cost"][model] = cost + self.user_dict[user]["model_cost"].get(model, 0)
else:
self.user_dict[user]["model_cost"] = {model: cost}
self._save_data_thread() # [Non-Blocking] Update persistent storage without blocking execution
return {"user": self.user_dict[user]}
def get_current_cost(self, user):
return self.user_dict[user].get("current_cost", 0)
def get_model_cost(self, user):
return self.user_dict[user].get("model_cost", 0)
View on GitHub (pinned to 6c2dcb801b)
Solutions
- Pass the full completion response: budget_manager.update_cost(user=user, completion_obj=response) so completion_cost can read model and usage.
- Or pass model plus the raw prompt text/messages so token counting can run.
- For streaming, accumulate the response first (or use the proxy's spend-tracking which handles this).
Example fix
# before
budget_manager.update_cost(user=user, model="gpt-4o", messages=[])
# after
budget_manager.update_cost(user=user, completion_obj=response)
# or: budget_manager.update_cost(user=user, model="gpt-4o", messages=[{"role": "user", "content": "hi"}]) Defensive patterns
Strategy: validation
Validate before calling
if completion_obj is None and not (model and messages):
raise ValueError("update_cost needs a completion object or model+messages") Type guard
def is_usable_completion(obj) -> bool:
return isinstance(obj, dict) and "model" in obj and "usage" in obj Prevention
- Call update_cost only after a completed, non-streaming response is in hand.
- For streaming, use LiteLLM's built-in success callbacks for spend tracking instead of manual update_cost.
- Assert response shape (model, usage keys) before passing to update_cost.
When it happens
Trigger: Calling budget_manager.update_cost(user='u', ...) without a completion_obj and without a usable (model, messages/text) pair — e.g. passing only kwargs like custom_pricing_entry but no response, or an empty messages list so both branches fall through to the else.
Common situations: Custom hook code calling update_cost at the wrong lifecycle point (before the response exists); passing a streaming chunk or an object without model/usage keys; refactoring that drops the completion argument.
Related errors
- duration needs to be one of ["daily", "weekly", "monthly", "
- 'models' param not in kwargs
- prompt_id is required for Langfuse prompt management
- otel.attributes must be a mapping with optional 'include_lis
- otel.attributes: include_list and exclude_list are mutually
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/d0b7fb28a6ef9134.
Report an issue: GitHub.