psf/requests · error · ValueError
Files must be provided.
Error message
Files must be provided.
What it means
Raised by `PreparedRequest._encode_files` in requests when it is asked to build a multipart/form-data body but the `files` argument is empty or falsy. The method exists solely to encode files (optionally merged with form data), so an empty `files` dict/list means the caller reached the multipart path by mistake. It is a guard against silently producing an empty multipart body.
Source
Thrown at src/requests/models.py:195
)
return urlencode(result, doseq=True)
else:
return data # type: ignore[return-value] # unreachable for valid _t.DataType
@staticmethod
def _encode_files(
files: _t.FilesType, data: _t.RawDataType | None
) -> tuple[bytes, str]:
"""Build the body for a multipart/form-data request.
Will successfully encode files when passed as a dict or a list of
tuples. Order is retained if data is a list of tuples but arbitrary
if parameters are supplied as a dict.
The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype)
or 4-tuples (filename, fileobj, contentype, custom_headers).
"""
if not files:
raise ValueError("Files must be provided.")
elif isinstance(data, basestring):
raise ValueError("Data must not be a string.")
new_fields: list[RequestField | tuple[str, bytes]] = []
fields = to_key_val_list(data or {})
files = to_key_val_list(files or {})
for field, val in fields:
if isinstance(val, basestring) or not hasattr(val, "__iter__"):
val = [val]
for v in val:
if v is not None:
# Don't call str() on bytestrings: in Py3 it all goes wrong.
if not isinstance(v, bytes):
v = str(v)
new_fields.append(
(View on GitHub ↗ (pinned to 414f0513c3)
Solutions
- Ensure the files mapping is non-empty before making the request: `if files: requests.post(url, files=files) else: requests.post(url, data=data)`.
- If you intended a plain form POST, pass `data=` only and omit `files` entirely.
- If you must send an empty multipart body deliberately, construct it yourself with `urllib3.filepost.encode_multipart_formdata` and set the body/headers manually.
Example fix
# before
files = collect_uploads() # may be {}
resp = requests.post(url, files=files)
# after
files = collect_uploads()
if files:
resp = requests.post(url, files=files)
else:
resp = requests.post(url, data=form_data) Defensive patterns
Strategy: validation
Validate before calling
# _encode_files raises when files is empty/None
if not files:
raise ValueError("no files to upload") # or just call requests without files=
resp = requests.post(url, files=files, data=data) Try / catch
try:
resp = requests.post(url, files=files)
except ValueError as e:
if "Files must be provided" in str(e):
... # caller passed empty files mapping
raise Prevention
- Only pass the files= kwarg when you actually have files; omit it entirely for plain form posts
- Check `if files:` before building a multipart request
- Don't pass an empty dict/list as files= — requests treats that as a programming error
When it happens
Trigger: Calling `requests.post(url, files={})` or `files=[]` (empty but the multipart path is still invoked internally in some flows), or calling `PreparedRequest._encode_files(files, data)` / `prepare_body` directly with a falsy `files` value while expecting multipart encoding. Also hit by test harnesses or wrappers that invoke `_encode_files` themselves.
Common situations: A file-upload helper builds the `files` dict dynamically (e.g. from a directory listing or user selection) and ends up empty; code passes `files=None`-vs-`files={}` inconsistently; frameworks that wrap requests and always route through the multipart encoder even when no files were attached.
Related errors
- Data must not be a string.
- Invalid URL {url!r}: No scheme supplied. Perhaps you meant h
- URL has an invalid label.
- Streamed bodies and files are mutually exclusive.
- Header part ({header_part!r}) from {header} must be of type
AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31).
Data as JSON: /data/errors/1dbcf8ed1b12d2cf.json.
Report an issue: GitHub ↗.