{"record":{"id":"09caf7687096f6ff","repo":"HKUDS/Vibe-Trading","slug":"start-date-start-must-be-before-end-date-end","errorCode":null,"errorMessage":"start_date {start} must be before end_date {end}","messagePattern":"start_date (.+?) must be before end_date (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/portfolio_risk_tool.py","lineNumber":161,"sourceCode":"        missing = [sym for sym in symbols if sym not in raw]\n        if missing:\n            raise ValueError(f\"weights missing basket symbols: {sorted(missing)}\")\n        return {sym: raw[sym] for sym in symbols}\n\n    @staticmethod\n    def _parse_dates(start_raw: Any, end_raw: Any) -> tuple[str, str]:\n        end = (\n            datetime.strptime(end_raw, \"%Y-%m-%d\").date()\n            if isinstance(end_raw, str) and end_raw\n            else datetime.now(timezone.utc).date()\n        )\n        start = (\n            datetime.strptime(start_raw, \"%Y-%m-%d\").date()\n            if isinstance(start_raw, str) and start_raw\n            else end - timedelta(days=_DEFAULT_LOOKBACK_DAYS)\n        )\n        if start >= end:\n            raise ValueError(f\"start_date {start} must be before end_date {end}\")\n        return start.isoformat(), end.isoformat()\n\n    @staticmethod\n    def _closes_frame(raw: Mapping[str, Any], symbols: list[str]) -> pd.DataFrame:\n        \"\"\"Shape the fetch envelope into a date-indexed close-price panel.\"\"\"\n        series: dict[str, pd.Series] = {}\n        for sym in symbols:\n            records = raw.get(sym)\n            if not records:\n                continue\n            times: list[Any] = []\n            prices: list[float] = []\n            for record in records:\n                if not isinstance(record, Mapping) or \"close\" not in record:\n                    continue\n                when = next((record[k] for k in _DATE_KEYS if k in record), None)\n                try:\n                    price = float(record[\"close\"])","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/portfolio_risk_tool.py#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure start_date is strictly earlier than end_date (equality is rejected)","If you only have an end date, omit start_date so the default lookback is applied","Verify both dates are ISO strings in YYYY-MM-DD format","Swap the arguments if you accidentally passed them in reverse order"],"exampleFix":"# before\nstart_date='2024-06-30', end_date='2024-06-30'\n\n# after\nstart_date='2024-06-29', end_date='2024-06-30'","handlingStrategy":"validation","validationCode":"from datetime import date, timedelta\nend = date.fromisoformat(end_date)\nstart = date.fromisoformat(start_date) if start_date else end - timedelta(days=90)\nif start >= end:\n    start = end - timedelta(days=1)  # or raise for the caller to fix","typeGuard":"def valid_date_range(start: str | None, end: str) -> bool:\n    try:\n        e = datetime.strptime(end, \"%Y-%m-%d\").date()\n        s = datetime.strptime(start, \"%Y-%m-%d\").date() if start else e - timedelta(days=90)\n        return s < e\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    result = tool.run(start_date=start, end_date=end)\nexcept ValueError as e:\n    if \"must be before\" in str(e):\n        start, end = min(start, end), max(start, end)  # auto-swap and retry","preventionTips":["Always pass dates as YYYY-MM-DD strings","Omit start_date when the default lookback window is acceptable","Assert start < end at the call site before invoking the tool"],"tags":["portfolio-risk","date-validation","dates"],"backgroundTag":"invalid-date-range","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}