aio-libs/aiohttp · error · ValueError
start cannot be after end
Error message
start cannot be after end
What it means
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).
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.
Example fix
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Defensive patterns
Strategy: try-catch
Validate before calling
start, end = parse_range(request.headers.get('Range'))
if start is not None and end is not None and start >= end:
raise web.HTTPRequestRangeNotSatisfiable() Try / catch
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Prevention
- 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.
When it happens
Trigger: Range: bytes=100-50, Range: bytes=10-9 — client supplies a start beyond the end.
Common situations: Off-by-one errors in custom download clients; buggy seek logic computing ranges; proxied requests from a tool that miscalculates suffix ranges.
Related errors
- range not in acceptable format
- No start or end of range specified
- 1007
- A ":" is not allowed in login (RFC 7617#section-2)
- A ":" is not allowed in username (RFC 1945#section-11.1)
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/429ff529e82b1179.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)