{"record":{"id":"1efd87f4188979a3","repo":"BerriAI/litellm","slug":"duration-needs-to-be-one-of-daily-weekly-m","errorCode":null,"errorMessage":"duration needs to be one of [\"daily\", \"weekly\", \"monthly\", \"yearly\"]","messagePattern":"duration needs to be one of \\[\"daily\", \"weekly\", \"monthly\", \"yearly\"\\]","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/budget_manager.py","lineNumber":92,"sourceCode":"        total_budget: float,\n        user: str,\n        duration: Literal[\"daily\", \"weekly\", \"monthly\", \"yearly\"] | None = None,\n        created_at: float = time.time(),\n    ):\n        self.user_dict[user] = {\"total_budget\": total_budget}\n        if duration is None:\n            return self.user_dict[user]\n\n        if duration == \"daily\":\n            duration_in_days = 1\n        elif duration == \"weekly\":\n            duration_in_days = DAYS_IN_A_WEEK\n        elif duration == \"monthly\":\n            duration_in_days = DAYS_IN_A_MONTH\n        elif duration == \"yearly\":\n            duration_in_days = DAYS_IN_A_YEAR\n        else:\n            raise ValueError(\"\"\"duration needs to be one of [\"daily\", \"weekly\", \"monthly\", \"yearly\"]\"\"\")\n        self.user_dict[user] = {\n            \"total_budget\": total_budget,\n            \"duration\": duration_in_days,\n            \"created_at\": created_at,\n            \"last_updated_at\": created_at,\n        }\n        self._save_data_thread()  # [Non-Blocking] Update persistent storage without blocking execution\n        return self.user_dict[user]\n\n    def projected_cost(self, model: str, messages: list, user: str):\n        text: Final = \"\".join(message[\"content\"] for message in messages)\n        prompt_tokens: Final = litellm.token_counter(model=model, text=text)\n        prompt_cost, _ = litellm.cost_per_token(model=model, prompt_tokens=prompt_tokens, completion_tokens=0)\n        current_cost: Final = self.user_dict[user].get(\"current_cost\", 0)\n        projected_cost: Final = prompt_cost + current_cost\n        return projected_cost\n\n    def get_total_budget(self, user: str):","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/budget_manager.py#L74-L110","documentation":"ValueError from BudgetManager.create_budget: the 'duration' argument must be one of the four literals 'daily', 'weekly', 'monthly', 'yearly'. The value is mapped to an internal day count (1/7/30/365) that drives budget-window reset; anything else — including abbreviations, plurals like 'days', or numeric durations — is rejected.","triggerScenarios":"Calling budget_manager.create_budget(total_budget=100, user='u', duration='30d') or duration='month', duration=30, duration=None-with-total_budget — any value not exactly matching one of the four literals. (duration=None takes a different branch and returns existing data.)","commonSituations":"Config files with '30d'/'1mo' style durations from other tools; passing a number of days expecting custom windows; casing issues like 'Monthly' (must be lowercase).","solutions":["Use an exact literal: duration='daily' | 'weekly' | 'monthly' | 'yearly'.","If durations come from config, map/validate them before calling create_budget.","For custom windows, file a feature request — only the four fixed windows are supported."],"exampleFix":"# before\nbudget_manager.create_budget(total_budget=10, user=\"u1\", duration=\"30d\")\n\n# after\nbudget_manager.create_budget(total_budget=10, user=\"u1\", duration=\"monthly\")","handlingStrategy":"validation","validationCode":"VALID_DURATIONS = {\"daily\", \"weekly\", \"monthly\", \"yearly\"}\nif duration not in VALID_DURATIONS:\n    raise ValueError(f\"duration must be one of {sorted(VALID_DURATIONS)}, got {duration!r}\")","typeGuard":"def is_valid_budget_duration(v) -> bool:\n    return isinstance(v, str) and v in {\"daily\", \"weekly\", \"monthly\", \"yearly\"}","tryCatchPattern":null,"preventionTips":["Map config strings (30d, 1mo) to the four literals in a normalizer before calling create_budget.","Cover budget creation in config tests so invalid durations fail at deploy time.","Remember casing matters: lowercase only."],"tags":["budget-manager","validation","configuration","python"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}