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

  1. Make 'percentage' a bare JSON number: {"percentage": 0.08}.
  2. Cast values with float() when building the payload programmatically.
  3. 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

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


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/27107590c27a3cb4. Report an issue: GitHub.