BerriAI/litellm · error · ValueError

Monthly resets currently only support 1 month intervals

Error message

Monthly resets currently only support 1 month intervals

What it means

The monthly-reset helper (_next_month_reset equivalent at duration_parser.py:375) only implements single-month reset cycles: when the parsed value (the count before 'mo') is anything other than 1, it raises. '2mo', '3mo' etc. are syntactically parseable but there is no defined monthly anchoring for multi-month windows aligned to the 1st of the month.

Source

Thrown at litellm/litellm_core_utils/duration_parser.py:375

    base_midnight: datetime,
    value: int,
    reset_time_of_day: time,
) -> datetime:
    """
    Handle monthly reset times. Resets land on the 1st at `reset_time_of_day`; if the
    1st of the current month at that time has already passed, roll to the 1st of next month.

    Args:
        current_time: Current datetime
        base_midnight: Midnight of current day
        value: Number of months (currently only supports 1 month resets)
        reset_time_of_day: Wall-clock time the reset lands on

    Returns:
        datetime: First day of the next reset month at `reset_time_of_day`
    """
    if value != 1:
        raise ValueError("Monthly resets currently only support 1 month intervals")

    first_of_this_month: Final = base_midnight.replace(day=1)
    candidate: Final = _apply_time_of_day(first_of_this_month, reset_time_of_day)
    if candidate <= current_time:
        return _apply_time_of_day(_first_of_next_month(first_of_this_month), reset_time_of_day)
    return candidate

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Use '1mo' and track multi-month windows at the application layer (aggregate monthly counters), or express the period in days ('90d', '365d') which resets on a rolling/aligned daily cycle instead.
  2. If you control the reset scheduling, compute the next reset datetime yourself and pass absolute times rather than relying on monthly parsing.
  3. Watch release notes — if multi-month support lands, update to the version that implements it.

Example fix

# before
budget_duration="3mo"    # quarterly -> raises, only 1 month supported

# after
budget_duration="90d"    # express quarter in days
# or "1mo" with app-side aggregation across 3 monthly windows
Defensive patterns

Strategy: validation

Validate before calling

def is_monthly_reset_supported(v: str) -> bool:
    return v.strip() in {"1mo", "mo", "month", "monthly"} or not v.strip().endswith(("mo",))

assert not is_monthly_reset_supported("3mo")

Type guard

def is_single_month_duration(v: object) -> bool:
    if not isinstance(v, str):
        return False
    s = v.strip().lower()
    return s != "mo" and (not s.endswith("mo") or s[:-2].isdigit() and s[:-2] == "1")

Try / catch

from litellm.litellm_core_utils.duration_parser import get_next_standardized_reset_time
try:
    nxt = get_next_standardized_reset_time(duration, now)
except ValueError as e:
    if "only support 1 month" in str(e):
        duration = duration.replace("mo", "d").replace("1", {3: "90", 6: "180", 12: "365"}.get(int(duration[:-2]), "30"), 1) if False else "30d"
        nxt = get_next_standardized_reset_time(duration, now)
    raise

Prevention

When it happens

Trigger: Setting budget_duration/reset duration to '2mo', '6mo', or '12mo' where the code path computes month-aligned standardized reset times (get_next_standardized_reset_time with monthly units). value != 1 trips the guard.

Common situations: Quarterly/annual budget windows ('3mo', '12mo') requested by finance teams; migrating from a custom cron that supported N-month cycles; copying enterprise plan examples that imply multi-month budgets are supported.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/cca8d3700cd1f4f5. Report an issue: GitHub.