Significant-Gravitas/AutoGPT · warning · HTTPException

`since` must be earlier than or equal to `until`.

Error message

`since` must be earlier than or equal to `until`.

What it means

Raised (422) by GET /usage/execution_costs (user cost summary) when both since and until query params are provided and since > until. The endpoint aggregates a time window; an inverted window is nonsensical and rejected before querying. top_runs_limit is separately constrained to [1,50] by Query(ge/le) metadata.

Source

Thrown at autogpt_platform/backend/backend/api/features/v1.py:2124

    user_id: Annotated[str, Security(get_user_id)],
    since: datetime | None = Query(
        None,
        description="Window start (UTC). Defaults to start of current calendar month.",
    ),
    until: datetime | None = Query(
        None,
        description="Window end (UTC). Defaults to now.",
    ),
    top_runs_limit: int = Query(
        10,
        ge=1,
        le=50,
        description="Maximum number of top-cost runs to return.",
    ),
) -> UserExecutionCostSummary:
    """Aggregated cost breakdown for the calling user's graph executions."""
    if since is not None and until is not None and since > until:
        raise HTTPException(
            status_code=422,
            detail="`since` must be earlier than or equal to `until`.",
        )
    return await get_user_cost_summary(
        user_id=user_id,
        since=since,
        until=until,
        top_runs_limit=top_runs_limit,
    )


@v1_router.get(
    path="/graphs/{graph_id}/executions",
    summary="List graph executions",
    tags=["graphs"],
    dependencies=[Security(requires_user)],
)
async def list_graph_executions(

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Validate/normalize the range client-side: if since > until, swap them or block submission.
  2. Check timezone handling — build both timestamps in the same (UTC) zone.
  3. Leave since/until omitted to default to a window ending now.

Example fix

// before
api.getCostSummary({ since: end, until: start }); // swapped

// after
const [lo, hi] = start <= end ? [start, end] : [end, start];
api.getCostSummary({ since: lo, until: hi });
Defensive patterns

Strategy: validation

Validate before calling

let [sinceN, untilN] = [since?.getTime() ?? -Infinity, until?.getTime() ?? Infinity];
if (sinceN > untilN) [sinceN, untilN] = [untilN, sinceN]; // or reject in the picker
await api.getCostSummary({ since: new Date(sinceN), until: new Date(untilN) });

Type guard

const isValidRange = (s?: Date, u?: Date) => !s || !u || s.getTime() <= u.getTime();

Try / catch

catch (e) { if (e.response?.status === 422 && /since/.test(e.response.data.detail)) { swapRangeAndRetry(); } else throw e; }

Prevention

When it happens

Trigger: Passing ?since=2026-08-10T00:00Z&until=2026-08-01T00:00Z — typically a date-picker allowing the user to select an end date before the start date, or timezone math that flips the boundaries.

Common situations: Analytics dashboards with free-form date range pickers; UTC-vs-local conversion bugs producing since one day after until; copy-pasted query params with swapped order.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/27715e9e24fff378. Report an issue: GitHub.