aio-libs/aiohttp · error · ValueError
range not in acceptable format
Error message
range not in acceptable format
What it means
The http_range property parses the Range header with the strict regex ^bytes=(\d*)-(\d*)$. If the header is present but does not match (re.findall returns no capture group, raising IndexError), aiohttp raises ValueError('range not in acceptable format'). This indicates a syntactically invalid Range from the client.
Solutions
- Catch ValueError around access to request.http_range and respond 416 Range Not Satisfiable.
- On invalid range, fall back to serving the full file (ignore the header).
- Document to clients that only single-range bytes= syntax is accepted.
Example fix
# before
rng = request.http_range # raises on bad header
# after
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Defensive patterns
Strategy: try-catch
Validate before calling
import re
rng = request.headers.get('Range')
if rng is not None and not re.match(r'^bytes=\d*-\d*$', rng):
raise web.HTTPRequestRangeNotSatisfiable() Try / catch
try:
rng = request.http_range
except ValueError:
raise web.HTTPRequestRangeNotSatisfiable() Prevention
- Always wrap request.http_range access in try/except ValueError.
- Decide upfront: ignore invalid Range or respond 416.
- Log malformed Range headers to spot misbehaving clients.
When it happens
Trigger: Range: items=0-100 (wrong unit), Range: bytes=abc-def (non-numeric), Range: 0-100 (missing 'bytes='), Range: bytes=1-2,3-4 (multiple ranges unsupported).
Common situations: Misbehaving crawlers/clients; custom download managers; CDNs forwarding non-standard Range syntax.
Related errors
- start cannot be after end
- 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/e7f97e93bd3ac5a4.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_request.py:613
parsed = parse_cookie_header(self.headers.get(hdrs.COOKIE, ""))
# Extract values from Morsel objects
return MappingProxyType({name: morsel.value for name, morsel in parsed})
@reify
def http_range(self) -> "slice[int, int, int]":
"""The content of Range HTTP header.
Return a slice instance.
"""
rng = self._headers.get(hdrs.RANGE)
start, end = None, None
if rng is not None:
try:
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")View on GitHub (pinned to d041d4d0fd)