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

  1. Ensure the files mapping is non-empty before making the request: `if files: requests.post(url, files=files) else: requests.post(url, data=data)`.
  2. If you intended a plain form POST, pass `data=` only and omit `files` entirely.
  3. 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

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


AI-assisted analysis of psf/requests@414f0513c3 (2026-07-31). Data as JSON: /data/errors/1dbcf8ed1b12d2cf.json. Report an issue: GitHub ↗.