psf/requests · error · UnrewindableBodyError
Unable to rewind request body for redirect.
Error message
Unable to rewind request body for redirect.
What it means
Raised by rewind_body() when either the request body has no seek() method (non-seekable) or _body_position was not recorded (is None or not an integer). Without both conditions, the body cannot be rewound for a redirect. This is distinct from error 53: here the capability is structurally absent, not broken at runtime.
Solutions
- Materialize the body into bytes before the request: data=b''.join(generator) or data=list(generator).
- Use a seekable BytesIO wrapper: from io import BytesIO; data=BytesIO(original_bytes).
- Set allow_redirects=False for non-rewindable bodies and handle redirects manually by re-creating the body each time.
Example fix
# before
def gen():
yield b'chunk1'
yield b'chunk2'
resp = session.post(url, data=gen()) # UnrewindableBodyError on redirect
# after — materialize
resp = session.post(url, data=b''.join(gen()))
# or disable redirects
resp = session.post(url, data=gen(), allow_redirects=False) Defensive patterns
Strategy: fallback
Validate before calling
# Materialize non-seekable bodies before redirecting requests body_bytes = b''.join(generator) if hasattr(generator, '__next__') else generator resp = session.post(url, data=body_bytes)
Type guard
def is_seekable(body) -> bool:
return hasattr(body, 'seek') and hasattr(body, 'read') Try / catch
try:
resp = session.post(url, data=body)
except requests.exceptions.UnrewindableBodyError:
# Fallback: disable redirects and handle manually
resp = session.post(url, data=body, allow_redirects=False) Prevention
- Materialize generators/iterators into bytes before posting if redirects are possible.
- Wrap seekable data in io.BytesIO for rewinding capability.
- Set allow_redirects=False when body cannot be replayed and handle redirects manually.
- Avoid piping/streaming sources as request bodies when redirects are enabled.
When it happens
Trigger: Redirect after a POST/PUT whose body is a generator, iterator, or other non-seekable object; a body from a pipe (subprocess stdout); a streaming source that was consumed and cannot be replayed; _body_position is None because the body was set after prepare.
Common situations: Posting a generator or iterator as the request body with redirects enabled; streaming data from a real-time source that can't be replayed; using data=iter([...]) instead of a seekable file or bytes; uploading from stdin or a socket.
Related errors
AI-assisted analysis of psf/requests@8068356288 (2026-08-11).
Data as JSON: /api/errors/e35d8ce52f5ac8c3.
Report an issue: GitHub.
Appendix: source
Thrown at src/requests/utils.py:1155
def rewind_body(prepared_request: PreparedRequest) -> None:
"""Move file pointer back to its recorded starting position
so it can be read again on redirect.
"""
body_seek = getattr(prepared_request.body, "seek", None)
if body_seek is not None and isinstance(
prepared_request._body_position,
integer_types,
):
try:
body_seek(prepared_request._body_position)
except OSError:
raise UnrewindableBodyError(
"An error occurred when rewinding request body for redirect."
)
else:
raise UnrewindableBodyError("Unable to rewind request body for redirect.")
View on GitHub (pinned to 8068356288)