{"id":"7bf577f67e583a76","repo":"aio-libs/aiohttp","slug":"no-start-or-end-of-range-specified","errorCode":null,"errorMessage":"No start or end of range specified","messagePattern":"No start or end of range specified","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"aiohttp/web_request.py","lineNumber":631,"sourceCode":"                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:\n        \"\"\"Return True if request has HTTP BODY, False otherwise.\"\"\"\n        return type(self._payload) is not EmptyStreamReader\n","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/web_request.py#L613-L649","documentation":"Raised as ValueError by BaseRequest.http_range (aiohttp/web_request.py:631) when the Range header matched the byte syntax but both captured groups were empty - i.e. literally 'Range: bytes=-'. aiohttp has no start and no end to compute a slice from, so the parsed range is meaningless and it raises rather than return a degenerate slice(None, None, 1).","triggerScenarios":"A client sending 'Range: bytes=-' (empty start and empty end) - typically a buggy download client or a probe. Because the regex '(\\d*)-(\\d*)' accepts empty captures, only the explicit None/None check on line 630 catches it.","commonSituations":"Misbehaving clients (some old Android download managers, curl edge cases); corrupted header from a proxy; manual tests that hardcode 'bytes=-'.","solutions":["Wrap request.http_range access in try/except ValueError and treat it as 'no range requested' (serve full body).","Return HTTPRequestedRangeNotSatisfiable (416) if you want strict semantics.","Audit upstream clients / proxies that emit 'bytes=-' to confirm they actually want a partial response."],"exampleFix":"// before\nrng = request.http_range  # raises on 'bytes=-'\n\n# after\ntry:\n    rng = request.http_range\nexcept ValueError:\n    rng = slice(None, None, 1)  # treat as full body","handlingStrategy":"try-catch","validationCode":"if request.headers.get(\"Range\") == \"bytes=-\":\n    rng = slice(None, None, 1)\nelse:\n    rng = request.http_range","typeGuard":"def is_meaningful_range(value: str) -> bool:\n    return value != \"bytes=-\"","tryCatchPattern":"try:\n    rng = request.http_range\nexcept ValueError:\n    rng = slice(None, None, 1)  # treat as full body","preventionTips":["Treat 'bytes=-' as 'no range' rather than an error if you want lenient behavior.","Audit upstream clients/proxies emitting degenerate ranges.","Document whether your endpoint returns 416 or the full body for empty ranges."],"tags":["http","range","validation","header-parsing"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}