{"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":"warning","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/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_request.py#L610-L646","documentation":"Raised as ValueError by BaseRequest.http_range (aiohttp/web_request.py:628) when the parsed Range header specifies a start byte that is greater than or equal to the (adjusted) end byte. Because HTTP ranges are inclusive but Python slices are exclusive, aiohttp adds 1 to end before comparing (line 625); if start >= end after that adjustment, the range is nonsensical and aiohttp refuses it.","triggerScenarios":"A Range header like 'bytes=100-100' becomes start=100, end=101 (inclusive->exclusive) and is fine; but 'bytes=100-50' yields start=100, end=51 -> start >= end -> ValueError. Also triggered by 'bytes=5-5' edge cases only when the math produces equality after the +1 step is bypassed.","commonSituations":"Client bugs computing Range from off-by-one indices; manual Range headers in tests; a download manager that retries a partial range with stale end values.","solutions":["Catch ValueError around request.http_range and respond with HTTPRequestedRangeNotSatisfiable (416) - which is the HTTP-standard response for an unsatisfiable range.","Validate start <= end in client code before emitting Range.","Let FileResponse handle Range natively instead of parsing it yourself - it already returns 416 for unsatisfiable ranges."],"exampleFix":"// before\nstart, end = request.http_range.start, request.http_range.stop\n\n# after\ntry:\n    rng = request.http_range\nexcept ValueError:\n    raise web.HTTPRequestRangeNotSatisfiable()","handlingStrategy":"try-catch","validationCode":"rng = request.headers.get(\"Range\", \"\")\nimport re\nm = re.match(r\"^bytes=(\\d+)-(\\d+)$\", rng)\nif m and int(m.group(1)) > int(m.group(2)):\n    raise web.HTTPRequestRangeNotSatisfiable()","typeGuard":"def range_start_le_end(value: str) -> bool:\n    import re\n    m = re.match(r\"^bytes=(\\d+)-(\\d+)$\", value)\n    return not m or int(m.group(1)) <= int(m.group(2))","tryCatchPattern":"try:\n    rng = request.http_range\nexcept ValueError:\n    raise web.HTTPRequestRangeNotSatisfiable()","preventionTips":["Return HTTP 416 for unsatisfiable ranges instead of letting ValueError propagate.","Validate start <= end in client code before emitting Range.","Prefer FileResponse's built-in Range handling."],"tags":["http","range","validation"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}