What happened?
With router_settings.provider_budget_config enabled, every /v1/embeddings and /v1/rerank call raises inside the budget callback:
LiteLLM:ERROR: litellm_logging.py:2672 - LiteLLM.LoggingError: [Non-Blocking] Exception occurred while success logging
File ".../litellm/litellm_core_utils/litellm_logging.py", line 2618, in async_success_handler
await callback.async_log_success_event(...)
File ".../litellm/router_strategy/budget_limiter.py", line 412, in async_log_success_event
raise ValueError("custom_llm_provider is required")
ValueError: custom_llm_provider is required
The error is labelled non-blocking and the request succeeds, so it looks cosmetic. It is not: RouterBudgetLimiting.async_log_success_event raises before _increment_spend_for_key, so embedding and rerank spend is never added to the provider budget. The ceiling silently under-counts, and a deployment that is mostly embeddings would never reach it.
/v1/chat/completions is unaffected — its spend does accumulate.
Root cause
budget_limiter.py:410 reads the provider from litellm_params:
custom_llm_provider: str = kwargs.get("litellm_params", {}).get("custom_llm_provider", None)
if custom_llm_provider is None:
raise ValueError("custom_llm_provider is required")
completion() puts it there (main.py:5289):
litellm_params = get_litellm_params(
...
custom_llm_provider=custom_llm_provider,
...
)
embedding() does not (main.py:6028):
litellm_params_dict = get_litellm_params(**kwargs)
custom_llm_provider is a named parameter of embedding(), so it is never in **kwargs; and it has already been resolved a few lines above by get_llm_provider() — it is passed to logging.update_environment_variables(custom_llm_provider=...) on the very next statement, just not into litellm_params.
Same shape at main.py:7548 (transcription) and main.py:7825 (speech). rerank_api/main.py:168 builds rerank_litellm_params without it as well.
Because the parameter is consumed by the function signature, no configuration can work around this — passing custom_llm_provider in the request body or in the deployment's litellm_params still ends up as the named argument, never in **kwargs.
Relevant log output
Reproduced 3/3 on /v1/embeddings and /v1/rerank; 0/4 on /v1/chat/completions (streaming and non-streaming, cache hit and miss).
GET /provider/budgets on the same proxy shows spend accumulating from chat only:
{"providers":{"bedrock":{"budget_limit":1000.0,"time_period":"1mo","spend":159.18631146,...}}}
Suggested fix
Pass the already-resolved value, as completion() does:
litellm_params_dict = get_litellm_params(custom_llm_provider=custom_llm_provider, **kwargs)
Config
model_list:
- model_name: titan-embed-v2
litellm_params:
model: bedrock/amazon.titan-embed-text-v2:0
aws_region_name: us-east-1
- model_name: cohere-rerank-v3-5
litellm_params:
model: bedrock/arn:aws:bedrock:us-east-1::foundation-model/cohere.rerank-v3-5:0
aws_region_name: us-east-1
router_settings:
provider_budget_config:
bedrock:
budget_limit: 1000
time_period: 1mo
redis_host: os.environ/REDIS_HOST
Twitter / LinkedIn details
No response
What happened?
With
router_settings.provider_budget_configenabled, every/v1/embeddingsand/v1/rerankcall raises inside the budget callback:The error is labelled non-blocking and the request succeeds, so it looks cosmetic. It is not:
RouterBudgetLimiting.async_log_success_eventraises before_increment_spend_for_key, so embedding and rerank spend is never added to the provider budget. The ceiling silently under-counts, and a deployment that is mostly embeddings would never reach it./v1/chat/completionsis unaffected — its spend does accumulate.Root cause
budget_limiter.py:410reads the provider fromlitellm_params:completion()puts it there (main.py:5289):embedding()does not (main.py:6028):custom_llm_provideris a named parameter ofembedding(), so it is never in**kwargs; and it has already been resolved a few lines above byget_llm_provider()— it is passed tologging.update_environment_variables(custom_llm_provider=...)on the very next statement, just not intolitellm_params.Same shape at
main.py:7548(transcription) andmain.py:7825(speech).rerank_api/main.py:168buildsrerank_litellm_paramswithout it as well.Because the parameter is consumed by the function signature, no configuration can work around this — passing
custom_llm_providerin the request body or in the deployment'slitellm_paramsstill ends up as the named argument, never in**kwargs.Relevant log output
Reproduced 3/3 on
/v1/embeddingsand/v1/rerank; 0/4 on/v1/chat/completions(streaming and non-streaming, cache hit and miss).GET /provider/budgetson the same proxy shows spend accumulating from chat only:Suggested fix
Pass the already-resolved value, as
completion()does:Config
Twitter / LinkedIn details
No response