{"record":{"id":"ca1b98ab89589bc8","repo":"sansan0/TrendRadar","slug":"error-ca1b98","errorCode":null,"errorMessage":"开始日期不能晚于结束日期","messagePattern":"开始日期不能晚于结束日期","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":null,"severity":"error","filePath":"mcp_server/utils/validators.py","lineNumber":447,"sourceCode":"        raise InvalidParameterError(\n            \"date_range 必须是字典类型、日期字符串或有效的JSON字符串\",\n            suggestion='例如: {\"start\": \"2025-10-01\", \"end\": \"2025-10-11\"} 或 \"2025-10-01\"'\n        )\n\n    start_str = date_range.get(\"start\")\n    end_str = date_range.get(\"end\")\n\n    if not start_str or not end_str:\n        raise InvalidParameterError(\n            \"date_range 必须包含 start 和 end 字段\",\n            suggestion='例如: {\"start\": \"2025-10-01\", \"end\": \"2025-10-11\"}'\n        )\n\n    start_date = validate_date(start_str)\n    end_date = validate_date(end_str)\n\n    if start_date > end_date:\n        raise InvalidParameterError(\n            \"开始日期不能晚于结束日期\",\n            suggestion=f\"start: {start_str}, end: {end_str}\"\n        )\n\n    # 检查日期是否在未来\n    today = datetime.now().date()\n    if start_date.date() > today or end_date.date() > today:\n        # 获取可用日期范围提示\n        try:\n            from ..services.data_service import DataService\n            data_service = DataService()\n            earliest, latest = data_service.get_available_date_range()\n\n            if earliest and latest:\n                available_range = f\"{earliest.strftime('%Y-%m-%d')} 至 {latest.strftime('%Y-%m-%d')}\"\n            else:\n                available_range = \"无可用数据\"\n        except Exception:","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/sansan0/TrendRadar/blob/8ee26026ba6c11dec41a95fb3895a7162876caa1/mcp_server/utils/validators.py#L429-L465","documentation":"Raised by normalize_date_range when both dates parse successfully but start_date > end_date — an inverted range. The suggestion echoes the offending pair. Note equality is allowed (single-day range), only strictly-later starts are rejected.","triggerScenarios":"Sending {\"start\": \"2025-10-11\", \"end\": \"2025-10-01\"}; swapping the keys; auto-generated ranges where the bound computation inverted (e.g. subtracting from the wrong end); timezone-crossing logic that produces off-by-one inversions near midnight.","commonSituations":"UI date pickers returning fields in display order (mm-dd) mapped to start/end wrongly; LLMs writing later date first; code computing start = today, end = today - N days by accident.","solutions":["Swap the values so start <= end","Compute end first, then start = end - timedelta(days=N), never the reverse unguarded","Add a client-side assert: if (start > end) [start, end] = [end, start]","Remember single-day queries can use start == end or the bare date string"],"exampleFix":"# before\n{\"date_range\": {\"start\": \"2025-10-11\", \"end\": \"2025-10-01\"}}\n# after\n{\"date_range\": {\"start\": \"2025-10-01\", \"end\": \"2025-10-11\"}}\n\n# defensive swap\nstart, end = min(start, end), max(start, end)","handlingStrategy":"validation","validationCode":"let [start, end] = [dr.start, dr.end];\nif (new Date(start) > new Date(end)) [start, end] = [end, start]; // or reject in UI","typeGuard":"const isOrderedRange = (s: string, e: string): boolean => s <= e; // ISO strings compare lexicographically","tryCatchPattern":"try { call({ date_range: dr }); }\ncatch (e) {\n  if (/开始日期不能晚于结束日期/.test(e.message)) call({ date_range: { start: dr.end, end: dr.start } });\n  else throw e;\n}","preventionTips":["ISO YYYY-MM-DD strings sort lexicographically — compare before sending","Derive start from end (end - N days) rather than independent computations","Date-picker UIs: enforce start<=end at input time"],"tags":["date-range","ordering","validation"],"backgroundTag":null,"analyzedSha":"8ee26026ba6c11dec41a95fb3895a7162876caa1","analyzedAt":"2026-08-15T01:42:18.084Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}