fix(proxy): treat malformed cost-map token limits as absent on /v1/models - #33904
Conversation
…dels create_model_info_response cast cost-map max_input_tokens / max_output_tokens with unguarded int(). The surrounding try/except covers only the get_model_info lookup, so a deployment whose model_info carries a non-numeric limit (e.g. "128,000" or an empty string) raised inside the per-model listing loop and failed the entire GET /v1/models and /models response with a 500, taking healthy deployments down with it. A deployment's model_info is registered into litellm.model_cost verbatim, so the malformed value reaches the cost map and not just the router index. Router.get_configured_token_limits already coerced this safely for the deployment path; the cost-map path was missed, so the two together still regressed. Both now share coerce_token_limit in litellm_core_utils, which returns None for a malformed value so the listing omits that one limit instead of failing, matching the graceful degradation the endpoint had before the cost-map switch. (cherry picked from commit ab02127)
Greptile SummaryThis PR backports a targeted fix for a 500 error on
Confidence Score: 5/5Safe to merge — the change is a one-directional tightening that degrades gracefully instead of raising, with no impact on well-formed deployments. The fix is minimal and surgical: No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/core_helpers.py | Adds coerce_token_limit() shared utility that safely converts token-limit values to int, treating booleans, non-numeric strings, and unexpected types as absent (None). |
| litellm/proxy/utils.py | Replaces bare int() calls on cost-map token limits with coerce_token_limit(), preventing a 500 when a deployment carries a non-numeric limit string like "128,000". |
| litellm/router.py | Removes the local _as_int helper from get_configured_token_limits and replaces it with the shared coerce_token_limit; functionally equivalent (both return None for booleans, non-numeric strings, and unsupported types). |
| tests/test_litellm/proxy/test_proxy_utils.py | Adds three new tests covering malformed cost-map limits (parametrized), mixed valid/malformed limits, and the end-to-end path where a Router registers a bad limit into litellm.model_cost; global state is correctly saved and restored. |
Reviews (1): Last reviewed commit: "fix(proxy): treat malformed cost-map tok..." | Re-trigger Greptile
Relevant issues
Backport of #33903 onto
patch-1.93.0rc2Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
patch-1.93.0rc2carries the regression, so the 1.93.0 stable cut ships a 500 onGET /v1/modelswithout this pickThe line also already carries the router-side guard from #33864, so it is in the same state staging was in: one path guarded, the cost-map path not
Runtime proof was captured on the staging PR (#33903) against a live proxy: 500 before, 200 with the malformed deployment's limits omitted after, plus a real OpenAI call through the same proxy. On this line, the picked commit resolves and degrades gracefully rather than raising, at db190ba
Type
🐛 Bug Fix
Changes
Clean cherry-pick of ab02127 from #33903, no conflicts and no adaptation needed for this line
create_model_info_responsecast the cost-mapmax_input_tokensandmax_output_tokenswith a bareint(), outside the try/except that guards theget_model_infolookup. A deployment whosemodel_infocarries a non-numeric limit therefore raised inside the per-model listing loop and failed the wholeGET /v1/modelsand/modelsresponse with a 500, taking every healthy deployment down with itBoth call sites now share
coerce_token_limitinlitellm_core_utils/core_helpers.py, which returnsNonefor anything that is not a usable number so the listing omits that one limit and keeps servingFinal Attestation