fix(duration_parser): raise ValueError for unparseable budget_duration

Replaces the silent fallback-to-next-midnight behavior in
get_next_standardized_reset_time when _parse_duration returns (None, None)
with a clear ValueError. Empty strings and unrecognized formats now raise
instead of silently scheduling a daily reset.

The validate_budget_duration guard in management endpoints already catches
this ValueError and converts it to HTTP 400, so no creation-path breakage
occurs. The budget reset job is hardened in a companion change so that
stale rows with budget_duration='' are skipped rather than wiped.

Fixes: BerriAI/litellm#39368
This commit is contained in:
Aftab 2026-09-03 13:36:17 +05:30
parent ed9d29a9b4
commit 2a266d764e

View file

@ -12,8 +12,6 @@ from datetime import datetime, time, timedelta, timezone, tzinfo
from typing import Final
from zoneinfo import ZoneInfo
from litellm._logging import verbose_logger
_BUDGET_DURATION_WORD_ALIASES: Final[dict[str, str]] = {
"hourly": "1h",
"daily": "24h",
@ -131,6 +129,9 @@ def get_next_standardized_reset_time(
Returns:
- Next reset time at a standardized interval in the specified timezone
Raises:
- ValueError: If the duration string is empty or cannot be parsed.
"""
# Set up timezone and normalize current time
current_time, _ = _setup_timezone(current_time, timezone_str)
@ -138,12 +139,10 @@ def get_next_standardized_reset_time(
# Parse duration
value, unit = _parse_duration(_normalize_duration(duration))
if value is None:
verbose_logger.warning(
"Unrecognized budget_duration %r; falling back to a next-midnight reset. "
"Use the <int><unit> format (e.g. '1h', '7d', '30d', '1mo').",
duration,
raise ValueError(
f"Invalid budget_duration {duration!r}. "
"Use the <int><unit> format (e.g. '1h', '7d', '30d', '1mo')."
)
return current_time.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(days=1)
# Midnight of the current day in the specified timezone
base_midnight: Final = current_time.replace(hour=0, minute=0, second=0, microsecond=0)