{"record":{"id":"b1060246b5cfd241","repo":"BerriAI/litellm","slug":"invalid-duration-format","errorCode":null,"errorMessage":"Invalid duration format","messagePattern":"Invalid duration format","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/duration_parser.py","lineNumber":33,"sourceCode":"from litellm._logging import verbose_logger\n\n_BUDGET_DURATION_WORD_ALIASES: Final[dict[str, str]] = {\n    \"hourly\": \"1h\",\n    \"daily\": \"24h\",\n    \"weekly\": \"7d\",\n    \"monthly\": \"30d\",\n}\n\n\ndef _normalize_duration(duration: str) -> str:\n    return _BUDGET_DURATION_WORD_ALIASES.get(duration.strip().lower(), duration)\n\n\ndef _extract_from_regex(duration: str) -> tuple[int, str]:\n    match: Final = re.match(r\"(\\d+)(mo|[smhdw]?)\", duration)\n\n    if not match:\n        raise ValueError(\"Invalid duration format\")\n\n    value, unit = match.groups()\n    value = int(value)\n\n    return value, unit\n\n\ndef get_last_day_of_month(year, month):\n    # Handle December case\n    if month == 12:\n        return 31\n    # Next month is January, so subtract a day from March 1st\n    next_month: Final = datetime(year=year, month=month + 1, day=1)\n    last_day_of_month: Final = (next_month - timedelta(days=1)).day\n    return last_day_of_month\n\n\ndef duration_in_seconds(duration: str) -> int:","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/duration_parser.py#L15-L51","documentation":"_extract_from_regex in litellm's duration parser matches durations against r'(\\d+)(mo|[smhdw]?)'. If re.match finds nothing — i.e. the string does not start with one or more digits — it raises 'Invalid duration format'. This is the first validation gate for budget/reset duration strings like '30d', '1mo', '12h'.","triggerScenarios":"Passing a duration with no leading digits: 'one day', 'day', '', 'sec', or 'monthly' after alias normalization fails ('monthly' is aliased to '30d', but an unlisted word like 'week' or 'biweekly' is not). Also negative or float inputs ('-1d', '0.5h') since the regex demands integer digits at position 0.","commonSituations":"Setting budget_duration in config.yaml from user input or a form field; upgrading from values that previously fell through to a default; locale-dependent words ('monat') not in the alias map.","solutions":["Use the supported integer+unit form: '<n>' (seconds), '10s', '10m', '10h', '10d', '10w', or '1mo'.","If free text is accepted upstream, map words through the alias table or your own dict before handing it to litellm, defaulting unknowns rather than passing them through.","Validate early with re.match(r'(\\d+)(mo|[smhdw]?)', value) and reject/normalize before the call."],"exampleFix":"# before\nbudget_duration=\"one day\"      # no leading digits -> raises\n\n# after\nbudget_duration=\"1d\"           # integer + supported unit\n# or pre-map: {\"day\":\"1d\", \"week\":\"1w\"}.get(user_input.lower(), \"30d\")","handlingStrategy":"validation","validationCode":"import re\n\n_DURATION_RE = re.compile(r\"^(\\d+)(mo|[smhdw]?)$\")\n\ndef is_parseable_duration(v: str) -> bool:\n    return bool(_DURATION_RE.match(v.strip()))\n\nassert is_parseable_duration(\"30d\")\nassert not is_parseable_duration(\"one day\")","typeGuard":"import re\n\ndef is_duration_string(v: object) -> bool:\n    \"\"\"True for litellm-supported '<int><unit>' durations (s/m/h/d/w/mo).\"\"\"\n    return isinstance(v, str) and bool(re.match(r\"^(\\d+)(mo|[smhdw]?)$\", v.strip()))","tryCatchPattern":"from litellm.litellm_core_utils.duration_parser import _extract_from_regex\ntry:\n    value, unit = _extract_from_regex(duration)\nexcept ValueError:\n    duration = \"30d\"  # explicit, logged default — never silently reinterpret\n    value, unit = _extract_from_regex(duration)","preventionTips":["Constrain duration inputs to a dropdown/enum of supported strings.","Wrap free-text durations in your own word->value map before passing through.","Anchored-regex validate at the config boundary (^...$) to catch trailing junk early."],"tags":["duration-parser","budget","validation","config"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}