HKUDS/Vibe-Trading · error · ValueError

start_date {start} must be before end_date {end}

Error message

start_date {start} must be before end_date {end}

What it means

Raised by PortfolioRiskTool._parse_dates when the resolved start date is on or after the end date. The end date comes from end_raw (parsed as %Y-%m-%d); the start is either the supplied start_raw or end minus _DEFAULT_LOOKBACK_DAYS. The tool needs a strictly positive, ordered window to compute returns.

Source

Thrown at agent/src/tools/portfolio_risk_tool.py:161

        missing = [sym for sym in symbols if sym not in raw]
        if missing:
            raise ValueError(f"weights missing basket symbols: {sorted(missing)}")
        return {sym: raw[sym] for sym in symbols}

    @staticmethod
    def _parse_dates(start_raw: Any, end_raw: Any) -> tuple[str, str]:
        end = (
            datetime.strptime(end_raw, "%Y-%m-%d").date()
            if isinstance(end_raw, str) and end_raw
            else datetime.now(timezone.utc).date()
        )
        start = (
            datetime.strptime(start_raw, "%Y-%m-%d").date()
            if isinstance(start_raw, str) and start_raw
            else end - timedelta(days=_DEFAULT_LOOKBACK_DAYS)
        )
        if start >= end:
            raise ValueError(f"start_date {start} must be before end_date {end}")
        return start.isoformat(), end.isoformat()

    @staticmethod
    def _closes_frame(raw: Mapping[str, Any], symbols: list[str]) -> pd.DataFrame:
        """Shape the fetch envelope into a date-indexed close-price panel."""
        series: dict[str, pd.Series] = {}
        for sym in symbols:
            records = raw.get(sym)
            if not records:
                continue
            times: list[Any] = []
            prices: list[float] = []
            for record in records:
                if not isinstance(record, Mapping) or "close" not in record:
                    continue
                when = next((record[k] for k in _DATE_KEYS if k in record), None)
                try:
                    price = float(record["close"])

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Ensure start_date is strictly earlier than end_date (equality is rejected)
  2. If you only have an end date, omit start_date so the default lookback is applied
  3. Verify both dates are ISO strings in YYYY-MM-DD format
  4. Swap the arguments if you accidentally passed them in reverse order

Example fix

# before
start_date='2024-06-30', end_date='2024-06-30'

# after
start_date='2024-06-29', end_date='2024-06-30'
Defensive patterns

Strategy: validation

Validate before calling

from datetime import date, timedelta
end = date.fromisoformat(end_date)
start = date.fromisoformat(start_date) if start_date else end - timedelta(days=90)
if start >= end:
    start = end - timedelta(days=1)  # or raise for the caller to fix

Type guard

def valid_date_range(start: str | None, end: str) -> bool:
    try:
        e = datetime.strptime(end, "%Y-%m-%d").date()
        s = datetime.strptime(start, "%Y-%m-%d").date() if start else e - timedelta(days=90)
        return s < e
    except ValueError:
        return False

Try / catch

try:
    result = tool.run(start_date=start, end_date=end)
except ValueError as e:
    if "must be before" in str(e):
        start, end = min(start, end), max(start, end)  # auto-swap and retry

Prevention

When it happens

Trigger: Passing start_date equal to end_date (start >= end triggers on equality), or start_date after end_date. Also occurs if dates are swapped (start later than end), or if unparseable date strings silently fall back to the default lookback and collide with end.

Common situations: Users swapping from/to parameters; same-day requests; timezone or off-by-one issues when generating date windows around 'today'; passing timestamps instead of YYYY-MM-DD strings.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/09caf7687096f6ff. Report an issue: GitHub.