{"record":{"id":"df681be044caa076","repo":"ruvnet/RuView","slug":"end-time-must-be-after-start-time","errorCode":null,"errorMessage":"End time must be after start time","messagePattern":"End time must be after start time","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"archive/v1/src/api/routers/pose.py","lineNumber":249,"sourceCode":"    except Exception as e:\n        logger.error(f\"Error getting zones summary: {e}\")\n        raise HTTPException(\n            status_code=500,\n            detail=\"An internal error occurred. Please try again later.\"\n        )\n\n\n@router.post(\"/historical\")\nasync def get_historical_data(\n    request: HistoricalDataRequest,\n    pose_service: PoseService = Depends(get_pose_service),\n    current_user: Dict = Depends(require_auth)\n):\n    \"\"\"Get historical pose estimation data.\"\"\"\n    try:\n        # Validate time range\n        if request.end_time <= request.start_time:\n            raise HTTPException(\n                status_code=400,\n                detail=\"End time must be after start time\"\n            )\n        \n        # Limit query range to prevent excessive data\n        max_range = timedelta(days=7)\n        if request.end_time - request.start_time > max_range:\n            raise HTTPException(\n                status_code=400,\n                detail=\"Query range cannot exceed 7 days\"\n            )\n        \n        data = await pose_service.get_historical_data(\n            start_time=request.start_time,\n            end_time=request.end_time,\n            zone_ids=request.zone_ids,\n            aggregation_interval=request.aggregation_interval,\n            include_raw_data=request.include_raw_data","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/api/routers/pose.py#L231-L267","documentation":"HTTP 400 from POST /pose/historical when end_time <= start_time in HistoricalDataRequest. The comparison uses <=, so equal timestamps are rejected too — the range must be strictly positive. The check runs on parsed datetimes inside the handler, before the 7-day cap and before the query.","triggerScenarios":"POST /pose/historical with end_time equal to or earlier than start_time — e.g. a 'today' preset that produces start == end at midnight, or from/to fields wired in reverse.","commonSituations":"Date-range pickers returning the same boundary instant for both ends; from/to fields swapped in the client; timezone conversion collapsing both values to the same instant.","solutions":["Fix or swap the fields so end_time > start_time","If equal timestamps occur naturally (same-instant presets), nudge end_time by one interval (e.g. +1 second)","Validate the range client-side before sending (see exampleFix)"],"exampleFix":"# before\nbody = {'start_time': iso_to, 'end_time': iso_from}  # swapped\n\n# after\nassert iso_from < iso_to, 'end_time must be after start_time'\nbody = {'start_time': iso_from, 'end_time': iso_to}","handlingStrategy":"validation","validationCode":"from datetime import timedelta\n\ndef valid_range(start, end):\n    if end <= start:\n        raise ValueError('end_time must be after start_time')\n    if end - start > timedelta(days=7):\n        raise ValueError('range must be <= 7 days')\n    return start, end","typeGuard":"def is_valid_time_range(start, end) -> bool:\n    return start is not None and end is not None and end > start","tryCatchPattern":"resp = await client.post('/pose/historical', json=body)\nif resp.status_code == 400 and 'after start time' in resp.json().get('detail', ''):\n    body['start_time'], body['end_time'] = min(body['start_time'], body['end_time']), max(body['start_time'], body['end_time'])\n    resp = await client.post('/pose/historical', json=body)\nresp.raise_for_status()","preventionTips":["Validate ranges in the date picker before enabling submit","Beware same-instant presets — nudge end by one interval","Disable submit buttons when both boundaries are equal"],"tags":["python","fastapi","validation","http-400","time-range","historical"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}