BerriAI/litellm · warning · HTTPException

Invalid budget_duration '{budget_duration}'. Use a format li

Error message

Invalid budget_duration '{budget_duration}'. Use a format like '1h', '24h', '7d', or '30d'.

What it means

validate_budget_duration guards the budget_duration field on key/user/team/budget creation: it parses the string with duration_in_seconds and computes the reset time via get_budget_reset_time inside try/except (ValueError, OverflowError), mapping any failure to HTTP 400 with the supported-format hint. It rejects three classes: unparseable strings ('1 month', 'monthly'), non-positive durations ('0h', '-30d' — which would resolve to a permanently-due reset time), and durations whose date arithmetic overflows (e.g. '999999999d').

Source

Thrown at litellm/proxy/management_endpoints/common_utils.py:46

    budget reset job.

    A non-positive duration also resolves to a reset time of "now", which leaves
    the row permanently due: the reset job re-reads it every tick and, once
    enough of them exist, they fill each batch and starve every other tenant's
    reset.
    """
    if budget_duration is None:
        return

    from litellm.litellm_core_utils.duration_parser import duration_in_seconds
    from litellm.proxy.common_utils.timezone_utils import get_budget_reset_time

    try:
        if duration_in_seconds(budget_duration) <= 0:
            raise ValueError("budget_duration must be positive")
        get_budget_reset_time(budget_duration=budget_duration)
    except (ValueError, OverflowError):
        raise HTTPException(
            status_code=400,
            detail={
                "error": f"Invalid budget_duration '{budget_duration}'. Use a format like '1h', '24h', '7d', or '30d'."
            },
        )


from litellm._logging import verbose_proxy_logger
from litellm.caching import DualCache
from litellm.proxy._types import (
    KeyRequestBase,
    LiteLLM_ManagementEndpoint_MetadataFields,
    LiteLLM_ManagementEndpoint_MetadataFields_Premium,
    LiteLLM_OrganizationTable,
    LiteLLM_ProjectTable,
    LiteLLM_TeamTable,
    LiteLLM_UserTable,
    LitellmUserRoles,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Use a supported plain value: '1h', '24h', '7d', '30d' (number + one time unit, no spaces)
  2. For non-positive or overflow mistakes, pick a real positive window within date-math limits
  3. Validate the format client-side with a regex like ^\d+[smhdw]$ before sending

Example fix

# before
curl -X POST http://localhost:4000/key/generate -d '{"budget_duration": "30days"}'
# 400 Invalid budget_duration '30days'. Use a format like '1h', '24h', '7d', or '30d'.

# after
curl -X POST http://localhost:4000/key/generate -d '{"budget_duration": "30d"}'
Defensive patterns

Strategy: validation

Validate before calling

import re

DURATION_RE = re.compile(r"^\d+[smhdw]$")

def valid_budget_duration(d: str | None) -> bool:
    if d is None:
        return True
    if not DURATION_RE.match(d):
        return False
    n = int(d[:-1])
    return n > 0 and n * 10_000 < 10**9  # stay far below date-math overflow

assert valid_budget_duration(payload.get("budget_duration")), "use '1h', '24h', '7d', or '30d'"

Type guard

from typing import TypeGuard
import re

_DURATION = re.compile(r"^([1-9]\d{0,6})[smhdw]$")

def is_valid_budget_duration(value: object) -> TypeGuard[str]:
    """Narrows to a positive, non-overflowing duration string the proxy accepts."""
    if not isinstance(value, str):
        return False
    m = _duration.match(value)
    return bool(m) and int(m.group(1)) > 0

Try / catch

import httpx

try:
    r = httpx.post(f"{PROXY_URL}/key/generate", json=payload, headers=hdrs)
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and "Invalid budget_duration" in e.response.text:
        payload["budget_duration"] = "30d"  # safe default; or surface to the user
        r = httpx.post(f"{PROXY_URL}/key/generate", json=payload, headers=hdrs)
        r.raise_for_status()
    else:
        raise

Prevention

When it happens

Trigger: POST /key/generate or /team/new with budget_duration='30days', '0h', '-1d', '1mo' (unsupported unit), or an absurd magnitude that overflows timedelta/date math; copying durations from cron or Kubernetes notation ('24h0m0s') which the parser does not accept.

Common situations: UIs offering free-text duration inputs; migrating configs from systems using ISO-8601 durations (P30D) or human phrases ('1 month'); users assuming months are supported because budgets are monthly.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/987eb9068627a8de. Report an issue: GitHub.