psf/requests · error · NotImplementedError
Streamed bodies and files are mutually exclusive.
Error message
Streamed bodies and files are mutually exclusive.
What it means
A `NotImplementedError` raised in `PreparedRequest.prepare_body` when `data` is a stream (a generator, iterator, or file-like object that isn't a str/bytes/list/tuple/dict) and `files` is also supplied. A streamed body is sent as-is (chunked or with Content-Length), while `files` requires building a multipart body in memory — the two body sources cannot be combined, and requests has no implementation to interleave them.
Source
Thrown at src/requests/models.py:623
length = super_len(data)
except (TypeError, AttributeError, UnsupportedOperation):
length = None
body = data
if getattr(body, "tell", None) is not None:
# Record the current file position before reading.
# This will allow us to rewind a file in the event
# of a redirect.
try:
self._body_position = body.tell() # type: ignore[union-attr] # guarded by getattr check
except OSError:
# This differentiates from None, allowing us to catch
# a failed `tell()` later when trying to rewind the body
self._body_position = object()
if files:
raise NotImplementedError(
"Streamed bodies and files are mutually exclusive."
)
if length:
self.headers["Content-Length"] = builtin_str(length)
else:
self.headers["Transfer-Encoding"] = "chunked"
else:
# After is_stream filtering, remaining data is raw (not streamed)
raw_data = cast("_t.RawDataType | None", data)
# Multi-part file uploads.
if files:
(body, content_type) = self._encode_files(files, raw_data)
else:
if raw_data:
body = self._encode_params(raw_data)
if isinstance(data, basestring) or _t.has_read(data):View on GitHub ↗ (pinned to 414f0513c3)
Solutions
- Pick one body style: for multipart uploads put the stream into `files` (`files={'file': open('big.bin','rb')}`) and drop `data=` or make it a field dict.
- For raw streaming uploads, use `data=fileobj` alone and set the Content-Type header yourself; do not pass `files`.
- For streaming multipart of large files, use `requests_toolbelt.MultipartEncoder` and pass it as `data=` with its content type header.
Example fix
# before
requests.post(url, data=open('big.bin', 'rb'), files={'meta': ('m.json', meta)})
# after
requests.post(url, files={'file': open('big.bin', 'rb'),
'meta': ('m.json', meta, 'application/json')}) Defensive patterns
Strategy: validation
Validate before calling
is_stream = data is not None and not isinstance(data, (str, bytes, dict, list, tuple))
if is_stream and files:
raise ValueError("pick one: a streamed body (file-like/generator data=) OR multipart files=") Type guard
def is_streamed_body(data):
return data is not None and not isinstance(data, (str, bytes, dict, list, tuple)) and (hasattr(data, "read") or hasattr(data, "__iter__")) Try / catch
try:
resp = requests.post(url, data=body_stream, files=files)
except NotImplementedError:
# can't mix a generator/file-like data= with files=; use multipart for everything
resp = requests.post(url, files={**files, "body": body_stream}) Prevention
- Choose one upload mode per request: streamed raw body (data=file/generator) or multipart (files=)
- To stream a file inside a multipart upload, pass the open file object inside files=, not data=
- Note this raises NotImplementedError, not ValueError — catch accordingly
When it happens
Trigger: `requests.post(url, data=open('big.bin','rb'), files={'f': fh})`, or `data=my_generator()` together with `files=`. Any call where `prepare_body` classifies `data` as iterable-but-not-basic (src/requests/models.py:602-603) while `files` is truthy.
Common situations: Upgrading an upload endpoint from streaming raw bytes to multipart and leaving the old file-object `data=` in place; passing a file handle to `data=` when the intent was `files=`; wrapper functions that forward both parameters unconditionally.
Related errors
- Files must be provided.
- Data must not be a string.
- chunk_size must be an int, it is instead a {type(chunk_size)
- The content for this response was already consumed
- An error occurred when rewinding request body for redirect.
AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31).
Data as JSON: /data/errors/4fc31d6ad99080ec.json.
Report an issue: GitHub ↗.