BerriAI/litellm · error · HTTPException

ptu_effective_to must be after ptu_effective_from

Error message

ptu_effective_to must be after ptu_effective_from

What it means

Cross-field invariant guard on PTU model_info: the effective window has ptu_effective_to not strictly after ptu_effective_from. Checked before the count/rate gate because a patch touching only one window end carries no count/rate and would otherwise let an inverted window persist, where the next router load drops the deployment.

Source

Thrown at litellm/proxy/management_endpoints/model_management_endpoints.py:322

def _validate_ptu_model_info(model_info: Mapping[str, object]) -> None:
    """Enforce the PTU cross-field invariant on the effective model_info.

    ptu_count and cost_per_ptu_per_hour must be set together, and a team_id and a
    ptu_effective_from are required when they are. The start is mandatory rather than
    defaulted because flat cost accrues from it: inferring one would let a deployment
    configured today be billed for days it did not exist. Per-field bounds (positive
    count, non-negative rate) are enforced by ModelInfo itself.

    Window ordering is checked before the count/rate gate. A patch that touches only one
    end of the window carries no count or rate, and ModelInfo sees one field at a time, so
    leaving it to either would let an inverted window reach the row; the next load then
    fails to parse it and drops the deployment out of the router, where no further patch
    can repair it because each one re-parses the stored value first.
    """
    effective_from: Final = _coerce_ptu_datetime(model_info.get("ptu_effective_from"))
    effective_to: Final = _coerce_ptu_datetime(model_info.get("ptu_effective_to"))
    if effective_from is not None and effective_to is not None and effective_to <= effective_from:
        raise HTTPException(status_code=400, detail="ptu_effective_to must be after ptu_effective_from")

    has_count: Final = model_info.get("ptu_count") is not None
    has_rate: Final = model_info.get("cost_per_ptu_per_hour") is not None
    if not has_count and not has_rate:
        return
    if has_count != has_rate:
        raise HTTPException(status_code=400, detail="ptu_count and cost_per_ptu_per_hour must be set together")
    if effective_from is None:
        raise HTTPException(
            status_code=400,
            detail=(
                "ptu_effective_from is required when PTU fields are set. Flat cost accrues from that "
                "instant, so without it the start would have to be inferred and a deployment configured "
                "today could be billed for days it did not exist"
            ),
        )
    if not model_info.get("team_id"):
        raise HTTPException(

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Set ptu_effective_to to a timestamp later than ptu_effective_from.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at litellm/proxy/management_endpoints/model_management_endpoints.py:322 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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