{"record":{"id":"cca8d3700cd1f4f5","repo":"BerriAI/litellm","slug":"monthly-resets-currently-only-support-1-month-inte","errorCode":null,"errorMessage":"Monthly resets currently only support 1 month intervals","messagePattern":"Monthly resets currently only support 1 month intervals","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/duration_parser.py","lineNumber":375,"sourceCode":"    base_midnight: datetime,\n    value: int,\n    reset_time_of_day: time,\n) -> datetime:\n    \"\"\"\n    Handle monthly reset times. Resets land on the 1st at `reset_time_of_day`; if the\n    1st of the current month at that time has already passed, roll to the 1st of next month.\n\n    Args:\n        current_time: Current datetime\n        base_midnight: Midnight of current day\n        value: Number of months (currently only supports 1 month resets)\n        reset_time_of_day: Wall-clock time the reset lands on\n\n    Returns:\n        datetime: First day of the next reset month at `reset_time_of_day`\n    \"\"\"\n    if value != 1:\n        raise ValueError(\"Monthly resets currently only support 1 month intervals\")\n\n    first_of_this_month: Final = base_midnight.replace(day=1)\n    candidate: Final = _apply_time_of_day(first_of_this_month, reset_time_of_day)\n    if candidate <= current_time:\n        return _apply_time_of_day(_first_of_next_month(first_of_this_month), reset_time_of_day)\n    return candidate\n","sourceCodeStart":357,"sourceCodeEnd":382,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/duration_parser.py#L357-L382","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","If you control the reset scheduling, compute the next reset datetime yourself and pass absolute times rather than relying on monthly parsing.","Watch release notes — if multi-month support lands, update to the version that implements it."],"exampleFix":"# before\nbudget_duration=\"3mo\"    # quarterly -> raises, only 1 month supported\n\n# after\nbudget_duration=\"90d\"    # express quarter in days\n# or \"1mo\" with app-side aggregation across 3 monthly windows","handlingStrategy":"validation","validationCode":"def is_monthly_reset_supported(v: str) -> bool:\n    return v.strip() in {\"1mo\", \"mo\", \"month\", \"monthly\"} or not v.strip().endswith((\"mo\",))\n\nassert not is_monthly_reset_supported(\"3mo\")","typeGuard":"def is_single_month_duration(v: object) -> bool:\n    if not isinstance(v, str):\n        return False\n    s = v.strip().lower()\n    return s != \"mo\" and (not s.endswith(\"mo\") or s[:-2].isdigit() and s[:-2] == \"1\")","tryCatchPattern":"from litellm.litellm_core_utils.duration_parser import get_next_standardized_reset_time\ntry:\n    nxt = get_next_standardized_reset_time(duration, now)\nexcept ValueError as e:\n    if \"only support 1 month\" in str(e):\n        duration = duration.replace(\"mo\", \"d\").replace(\"1\", {3: \"90\", 6: \"180\", 12: \"365\"}.get(int(duration[:-2]), \"30\"), 1) if False else \"30d\"\n        nxt = get_next_standardized_reset_time(duration, now)\n    raise","preventionTips":["Reject Nmo where N != 1 at config validation time.","Encode quarterly/annual windows in days (90d/365d) or app-level counters.","Track litellm release notes for multi-month reset support."],"tags":["duration-parser","budget","monthly-reset","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}