{"record":{"id":"8b4c80c4377a8146","repo":"BerriAI/litellm","slug":"invalid-budget-reset-time-raw-r-must-be-a-quote","errorCode":null,"errorMessage":"Invalid budget_reset_time {raw!r}; must be a quoted 24-hour 'HH:MM' string, e.g. \"12:00\"","messagePattern":"Invalid budget_reset_time (.+?); must be a quoted 24-hour 'HH:MM' string, e\\.g\\. \"12:00\"","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/common_utils/timezone_utils.py","lineNumber":32,"sourceCode":"    module-level globals at call time.\n    \"\"\"\n\n    model_config = ConfigDict(frozen=True)\n\n    timezone: str = \"UTC\"\n    reset_time_of_day: time = time(0, 0)\n\n\ndef parse_budget_reset_time(raw: object) -> time:\n    \"\"\"Parse a `budget_reset_time` config value (e.g. \"12:00\") into a `time`.\n\n    Falls back to midnight when unset; raises a clear error on a malformed value\n    so a bad config fails loudly at startup instead of silently resetting at midnight.\n    \"\"\"\n    if raw is None or raw == \"\":\n        return time(0, 0)\n    if not isinstance(raw, str):\n        raise ValueError(f\"Invalid budget_reset_time {raw!r}; must be a quoted 24-hour 'HH:MM' string, e.g. \\\"12:00\\\"\")\n    for fmt in (\"%H:%M\", \"%H:%M:%S\"):\n        try:\n            parsed = datetime.strptime(raw, fmt)\n            return time(hour=parsed.hour, minute=parsed.minute, second=parsed.second)\n        except ValueError:\n            continue\n    raise ValueError(\n        f\"Invalid budget_reset_time {raw!r}; expected a 24-hour 'HH:MM' or 'HH:MM:SS' string, e.g. \\\"12:00\\\"\"\n    )\n\n\ndef get_budget_reset_timezone() -> str:\n    \"\"\"\n    Get the budget reset timezone from litellm_settings.\n    Falls back to UTC if not specified.\n\n    litellm_settings values are set as attributes on the litellm module\n    by proxy_server.py at startup (via setattr(litellm, key, value)).","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/common_utils/timezone_utils.py#L14-L50","documentation":"ValueError from parse_budget_reset_time in litellm/proxy/common_utils/timezone_utils.py when the budget_reset_time config value is present but is not a string. The parser deliberately fails loudly at startup instead of silently resetting budgets at midnight. Non-string values almost always come from YAML type coercion (e.g. an unquoted 12:00 in YAML 1.1 is parsed as a sexagesimal integer) or from passing a datetime.time object where a quoted string is expected.","triggerScenarios":"Setting budget_reset_time: 12:00 unquoted in proxy_config.yaml (PyYAML parses it as the int 720, not the string '12:00'); passing budget_reset_time as a datetime.time object programmatically; passing a number (budget_reset_time: 0) or any non-string YAML scalar to a virtual key budget config.","commonSituations":"Copying an example config that shows budget_reset_time: 12:00 without quotes; upgrading LiteLLM to a version that added strict validation where previously the value was ignored; generating config programmatically with yaml.dump of a time object.","solutions":["Quote the value in YAML: budget_reset_time: \"12:00\".","If building the config in Python, serialize with .strftime('%H:%M') or .isoformat() before dumping to YAML.","Remove the budget_reset_time key entirely if midnight (00:00) is acceptable — None and empty string both fall back to midnight without erroring."],"exampleFix":"# before (YAML 1.1 parses 12:00 as int 720 -> ValueError)\nbudget_reset_time: 12:00\n\n# after\nbudget_reset_time: \"12:00\"","handlingStrategy":"validation","validationCode":"from datetime import time\n\ndef validate_budget_reset_time(raw) -> time:\n    if raw is None or raw == \"\":\n        return time(0, 0)\n    if not isinstance(raw, str):\n        raise TypeError(\"budget_reset_time must be a quoted string like '12:00'\")\n    return raw  # string ok, format checked separately","typeGuard":"def is_valid_budget_reset_time_value(raw: object) -> bool:\n    return raw is None or raw == \"\" or (isinstance(raw, str) and len(raw) >= 4 and raw[2] == \":\")","tryCatchPattern":"try:\n    from litellm.proxy.common_utils.timezone_utils import parse_budget_reset_time\n    reset_at = parse_budget_reset_time(cfg.get(\"budget_reset_time\"))\nexcept ValueError as e:\n    raise SystemConfigError(f\"bad budget_reset_time: {e}\") from e","preventionTips":["Always quote HH:MM values in YAML configs ('budget_reset_time: \"12:00\"').","When generating configs from Python, strftime('%H:%M') before yaml.dump.","Lint proxy configs in CI with a schema that types budget_reset_time as string-pattern '^([01]\\\\d|2[0-3]):[0-5]\\\\d(:[0-5]\\\\d)?$'."],"tags":["litellm-proxy","config","yaml","validation","budget"],"backgroundTag":"config-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}