{"record":{"id":"14fc7be18d5a3b4f","repo":"BerriAI/litellm","slug":"discount-for-provider-must-be-a-number","errorCode":null,"errorMessage":"Discount for {provider} must be a number","messagePattern":"Discount for (.+?) must be a number","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"litellm/proxy/management_endpoints/cost_tracking_settings.py","lineNumber":229,"sourceCode":"\n    # Validate that all providers are valid LiteLLM providers\n    invalid_providers: Final = []\n    for provider in cost_discount_config:\n        if provider not in LlmProvidersSet:\n            invalid_providers.append(provider)\n\n    if invalid_providers:\n        raise HTTPException(\n            status_code=400,\n            detail={\n                \"error\": f\"Invalid provider(s): {', '.join(invalid_providers)}. Must be valid LiteLLM providers. See https://docs.litellm.ai/docs/providers for the full list.\"\n            },\n        )\n\n    # Validate discount values are between 0 and 1\n    for provider, discount in cost_discount_config.items():\n        if not isinstance(discount, (int, float)):\n            raise HTTPException(status_code=400, detail=f\"Discount for {provider} must be a number\")\n        if not (0 <= discount <= 1):\n            raise HTTPException(\n                status_code=400,\n                detail=f\"Discount for {provider} must be between 0 and 1 (0% to 100%)\",\n            )\n\n    try:\n        # Load existing config\n        config: Final = await proxy_config.get_config()\n\n        # Ensure litellm_settings exists\n        if \"litellm_settings\" not in config:\n            config[\"litellm_settings\"] = {}\n\n        # Update cost_discount_config\n        config[\"litellm_settings\"][\"cost_discount_config\"] = cost_discount_config\n\n        # Save the updated config to DB","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/cost_tracking_settings.py#L211-L247","documentation":"Validation error (HTTP 400) from PATCH /config/cost_discount_config: the value for each provider key must be an int or float. Python's isinstance check rejects strings like \"0.1\", booleans aside, None, lists, or nested objects.","triggerScenarios":"Sending the discount as a quoted string (\"discount\": \"0.10\") — very common when values come from environment variables or YAML-as-string; sending null or an object {\"discount\": 0.1} instead of a bare number.","commonSituations":"Values round-tripped through env vars or templates that stringify numbers; JSON built by string concatenation; config pipelines that emit strings for all scalars.","solutions":["Send a bare JSON number: {\"openai\": 0.10} not {\"openai\": \"0.10\"}.","If generating the payload programmatically, cast with float()/int() before serializing.","Check for accidental nulls or nested dicts in the payload."],"exampleFix":"# before\n{\"openai\": \"0.10\"}   # 400 Discount for openai must be a number\n\n# after\n{\"openai\": 0.10}","handlingStrategy":"type-guard","validationCode":"cleaned = {}\nfor provider, discount in raw_config.items():\n    if isinstance(discount, str):\n        discount = float(discount)  # coerce stringified numbers, or fail loudly\n    if not isinstance(discount, (int, float)) or isinstance(discount, bool):\n        raise TypeError(f\"{provider}: discount must be a number, got {type(discount).__name__}\")\n    cleaned[provider] = discount","typeGuard":"def is_numeric_discount(cfg: dict) -> bool:\n    return all(\n        isinstance(v, (int, float)) and not isinstance(v, bool)\n        for v in cfg.values()\n    )","tryCatchPattern":null,"preventionTips":["Never quote numeric values in JSON payloads.","Cast env-var/template-sourced values with float() before serializing.","Reject bools explicitly — bool is a subclass of int in Python."],"tags":["litellm-proxy","validation","type-error","cost-tracking"],"backgroundTag":"type-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}