{"record":{"id":"429ff529e82b1179","repo":"aio-libs/aiohttp","slug":"start-cannot-be-after-end","errorCode":null,"errorMessage":"start cannot be after end","messagePattern":"start cannot be after end","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/web_request.py","lineNumber":628,"sourceCode":"                pattern = r\"^bytes=(\\d*)-(\\d*)$\"\n                start, end = re.findall(pattern, rng, re.ASCII)[0]\n            except IndexError:  # pattern was not found in header\n                raise ValueError(\"range not in acceptable format\")\n\n            end = int(end) if end else None\n            start = int(start) if start else None\n\n            if start is None and end is not None:\n                # end with no start is to return tail of content\n                start = -end\n                end = None\n\n            if start is not None and end is not None:\n                # end is inclusive in range header, exclusive for slice\n                end += 1\n\n                if start >= end:\n                    raise ValueError(\"start cannot be after end\")\n\n            if start is end is None:  # No valid range supplied\n                raise ValueError(\"No start or end of range specified\")\n\n        return slice(start, end, 1)\n\n    @reify\n    def content(self) -> StreamReader:\n        \"\"\"Return raw payload stream.\"\"\"\n        return self._payload\n\n    @property\n    def can_read_body(self) -> bool:\n        \"\"\"Return True if request's HTTP BODY can be read, False otherwise.\"\"\"\n        return not self._payload.at_eof()\n\n    @reify\n    def body_exists(self) -> bool:","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/web_request.py#L610-L646","documentation":"After converting the inclusive Range end to an exclusive bound (end += 1), http_range checks start >= end and raises ValueError('start cannot be after end'). E.g. Range: bytes=100-100 yields start=100,end=101 (valid), but bytes=100-50 fails because start (100) >= end (51).","triggerScenarios":"Range: bytes=100-50, Range: bytes=10-9 — client supplies a start beyond the end.","commonSituations":"Off-by-one errors in custom download clients; buggy seek logic computing ranges; proxied requests from a tool that miscalculates suffix ranges.","solutions":["Catch ValueError around request.http_range and respond 416.","Serve the full content when the range is logically invalid.","Validate start < end client-side before sending the Range header."],"exampleFix":"try:\n    rng = request.http_range\nexcept ValueError:\n    raise web.HTTPRequestRangeNotSatisfiable()","handlingStrategy":"try-catch","validationCode":"start, end = parse_range(request.headers.get('Range'))\nif start is not None and end is not None and start >= end:\n    raise web.HTTPRequestRangeNotSatisfiable()","typeGuard":null,"tryCatchPattern":"try:\n    rng = request.http_range\nexcept ValueError:\n    raise web.HTTPRequestRangeNotSatisfiable()","preventionTips":["Wrap http_range access in try/except ValueError.","Respond 416 when start >= end rather than crashing the handler.","Validate client range math before sending requests."],"tags":["range","validation","http-header","fileresponse"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}