{"record":{"id":"a70f8df695e1fe74","repo":"BerriAI/litellm","slug":"margin-for-provider-must-be-a-number-percentage","errorCode":null,"errorMessage":"Margin for {provider} must be a number (percentage) or dict with 'percentage' and/or 'fixed_amount'","messagePattern":"Margin for (.+?) must be a number \\(percentage\\) or dict with 'percentage' and/or 'fixed_amount'","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"litellm/proxy/management_endpoints/cost_tracking_settings.py","lineNumber":405,"sourceCode":"            if \"fixed_amount\" in margin_value:\n                fixed_amount = margin_value[\"fixed_amount\"]\n                if not isinstance(fixed_amount, (int, float)):\n                    raise HTTPException(\n                        status_code=400,\n                        detail=f\"Fixed margin amount for {provider} must be a number\",\n                    )\n                if fixed_amount < 0:\n                    raise HTTPException(\n                        status_code=400,\n                        detail=f\"Fixed margin amount for {provider} must be non-negative\",\n                    )\n            if not margin_value:  # Empty dict\n                raise HTTPException(\n                    status_code=400,\n                    detail=f\"Margin config for {provider} cannot be empty. Must include 'percentage' and/or 'fixed_amount'\",\n                )\n        else:\n            raise HTTPException(\n                status_code=400,\n                detail=f\"Margin for {provider} must be a number (percentage) or dict with 'percentage' and/or 'fixed_amount'\",\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_margin_config\n        config[\"litellm_settings\"][\"cost_margin_config\"] = cost_margin_config\n\n        # Save the updated config to DB\n        await proxy_config.save_config(new_config=config)\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/management_endpoints/cost_tracking_settings.py#L387-L423","documentation":"Validation error (HTTP 400) from PATCH /config/cost_margin_config: a provider's margin value is neither a number (simple percentage format) nor a dict (complex format with 'percentage'/'fixed_amount') — e.g. a string, list, or null. This is the fallback branch after the isinstance checks for (int, float) and dict both fail.","triggerScenarios":"Sending {\"openai\": \"0.08\"}, {\"openai\": null}, {\"openai\": [0.08]}, or a boolean; stringified values from config pipelines.","commonSituations":"JSON payloads built from untyped sources where scalars become strings or nulls; booleans sneaking in from feature-flag systems (bool is not int-checked here for the dict path but True would pass the numeric branch as 1 — the string/list cases are the real hits).","solutions":["Send either a bare number ({\"openai\": 0.08}) or a dict ({\"openai\": {\"percentage\": 0.08}}).","Never send null, strings, or arrays as the margin value.","Run the payload through a JSON schema check before submitting (see type guard)."],"exampleFix":"# before\n{\"openai\": \"0.08\"}\n\n# after\n{\"openai\": 0.08}","handlingStrategy":"type-guard","validationCode":"def normalize_margin(v):\n    if isinstance(v, str):\n        v = float(v)\n    if isinstance(v, dict):\n        assert {\"percentage\", \"fixed_amount\"} & set(v), \"dict needs percentage/fixed_amount\"\n    else:\n        assert isinstance(v, (int, float)) and not isinstance(v, bool), \"must be number or dict\"\n    return v\n\ncost_margin_config = {k: normalize_margin(v) for k, v in cost_margin_config.items()}","typeGuard":"from typing import Union\n\ndef is_margin_value(v: object) -> bool:\n    if isinstance(v, bool) or v is None:\n        return False\n    if isinstance(v, (int, float)):\n        return True\n    return isinstance(v, dict) and bool({\"percentage\", \"fixed_amount\"} & set(v))","tryCatchPattern":null,"preventionTips":["Constrain the margin union type (number | {percentage?, fixed_amount?}) in your own schemas.","Validate with jsonschema/pydantic before sending to the proxy."],"tags":["litellm-proxy","validation","type-error","cost-tracking"],"backgroundTag":"schema-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}