BerriAI/litellm · warning · HTTPException
Margin percentage for {provider} must be a number
Error message
Margin percentage for {provider} must be a number What it means
Validation error (HTTP 400) from PATCH /config/cost_margin_config when using the complex dict format ({"percentage": ..., "fixed_amount": ...}) and the 'percentage' field is present but not an int/float (e.g. the string "0.08" or null).
Source
Thrown at litellm/proxy/management_endpoints/cost_tracking_settings.py:378
"error": f"Invalid provider(s): {', '.join(invalid_providers)}. Must be valid LiteLLM providers or 'global'. See https://docs.litellm.ai/docs/providers for the full list."
},
)
# Validate margin values
for provider, margin_value in cost_margin_config.items():
if isinstance(margin_value, (int, float)):
# Simple percentage format: {"openai": 0.10}
if not (0 <= margin_value <= 10): # Allow up to 1000% margin
raise HTTPException(
status_code=400,
detail=f"Margin percentage for {provider} must be between 0 and 10 (0% to 1000%)",
)
elif isinstance(margin_value, dict):
# Complex format: {"percentage": 0.08, "fixed_amount": 0.0005}
if "percentage" in margin_value:
percentage = margin_value["percentage"]
if not isinstance(percentage, (int, float)):
raise HTTPException(
status_code=400,
detail=f"Margin percentage for {provider} must be a number",
)
if not (0 <= percentage <= 10):
raise HTTPException(
status_code=400,
detail=f"Margin percentage for {provider} must be between 0 and 10 (0% to 1000%)",
)
if "fixed_amount" in margin_value:
fixed_amount = margin_value["fixed_amount"]
if not isinstance(fixed_amount, (int, float)):
raise HTTPException(
status_code=400,
detail=f"Fixed margin amount for {provider} must be a number",
)
if fixed_amount < 0:
raise HTTPException(
status_code=400,View on GitHub (pinned to 77b7c6c40c)
Solutions
- Make 'percentage' a bare JSON number: {"percentage": 0.08}.
- Cast values with float() when building the payload programmatically.
- Omit 'percentage' entirely if you only want fixed_amount.
Example fix
# before
{"openai": {"percentage": "0.08"}}
# after
{"openai": {"percentage": 0.08}} Defensive patterns
Strategy: type-guard
Validate before calling
for provider, margin in cost_margin_config.items():
if isinstance(margin, dict) and "percentage" in margin:
p = margin["percentage"]
if isinstance(p, str):
margin["percentage"] = p = float(p) # coerce or fail
assert isinstance(p, (int, float)) and not isinstance(p, bool), f"{provider}.percentage must be numeric" Type guard
def is_valid_margin_dict(m: dict) -> bool:
ok = True
if "percentage" in m:
ok &= isinstance(m["percentage"], (int, float)) and not isinstance(m["percentage"], bool)
if "fixed_amount" in m:
ok &= isinstance(m["fixed_amount"], (int, float)) and not isinstance(m["fixed_amount"], bool)
return ok and bool(m) Prevention
- Keep 'percentage' and 'fixed_amount' as bare JSON numbers.
- Run a payload schema check (e.g. jsonschema) before PATCHing.
When it happens
Trigger: Sending {"openai": {"percentage": "0.08", "fixed_amount": 0.001}} — the percentage arrived stringified from env vars, templates, or YAML string coercion.
Common situations: Config generators that stringify all scalars; hand-written JSON with quotes around numbers; copy-paste from documentation examples where quotes slipped in.
Related errors
- Discount for {provider} must be a number
- Fixed margin amount for {provider} must be a number
- Margin for {provider} must be a number (percentage) or dict
- Invalid provider(s): {', '.join(invalid_providers)}. Must be
- Discount for {provider} must be between 0 and 1 (0% to 100%)
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/27107590c27a3cb4.
Report an issue: GitHub.