aio-libs/aiohttp · warning · ValueError
start cannot be after end
Error message
start cannot be after end
What it means
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.
Source
Thrown at aiohttp/web_request.py:628
pattern = r"^bytes=(\d*)-(\d*)$"
start, end = re.findall(pattern, rng, re.ASCII)[0]
except IndexError: # pattern was not found in header
raise ValueError("range not in acceptable format")
end = int(end) if end else None
start = int(start) if start else None
if start is None and end is not None:
# end with no start is to return tail of content
start = -end
end = None
if start is not None and end is not None:
# end is inclusive in range header, exclusive for slice
end += 1
if start >= end:
raise ValueError("start cannot be after end")
if start is end is None: # No valid range supplied
raise ValueError("No start or end of range specified")
return slice(start, end, 1)
@reify
def content(self) -> StreamReader:
"""Return raw payload stream."""
return self._payload
@property
def can_read_body(self) -> bool:
"""Return True if request's HTTP BODY can be read, False otherwise."""
return not self._payload.at_eof()
@reify
def body_exists(self) -> bool:View on GitHub (pinned to c0ef574e29)
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.
Example fix
// before
start, end = request.http_range.start, request.http_range.stop
# after
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Defensive patterns
Strategy: try-catch
Validate before calling
rng = request.headers.get("Range", "")
import re
m = re.match(r"^bytes=(\d+)-(\d+)$", rng)
if m and int(m.group(1)) > int(m.group(2)):
raise web.HTTPRequestRangeNotSatisfiable() Type guard
def range_start_le_end(value: str) -> bool:
import re
m = re.match(r"^bytes=(\d+)-(\d+)$", value)
return not m or int(m.group(1)) <= int(m.group(2)) Try / catch
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- range not in acceptable format
- No start or end of range specified
- Method cannot contain non-token characters {method!r} (found
- Invalid Content-Length header: {content_length_hdr!r}
- compress must be one of True, False, 'deflate', or 'gzip'
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/429ff529e82b1179.json.
Report an issue: GitHub.