boto/boto3 · error · ValueError

Filename must be a string or a path-like object

Error message

Filename must be a string or a path-like object

What it means

Raised by S3Transfer.upload_file after it converts pathlib path-likes to strings and confirms the result is a str. upload_file takes a filesystem path (not a file object) and streams it to S3; if the value is neither a str nor an os.PathLike it cannot be opened, so boto3 rejects it with this ValueError before attempting any transfer.

Source

Thrown at boto3/s3/transfer.py:445

        else:
            self._manager = create_transfer_manager(client, config, osutil)

    def upload_file(
        self, filename, bucket, key, callback=None, extra_args=None
    ):
        """Upload a file to an S3 object.

        Variants have also been injected into S3 client, Bucket and Object.
        You don't have to use S3Transfer.upload_file() directly.

        .. seealso::
            :py:meth:`S3.Client.upload_file`
            :py:meth:`S3.Client.upload_fileobj`
        """
        if isinstance(filename, PathLike):
            filename = fspath(filename)
        if not isinstance(filename, str):
            raise ValueError('Filename must be a string or a path-like object')

        subscribers = self._get_subscribers(callback)
        future = self._manager.upload(
            filename, bucket, key, extra_args, subscribers
        )
        try:
            future.result()
        # If a client error was raised, add the backwards compatibility layer
        # that raises a S3UploadFailedError. These specific errors were only
        # ever thrown for upload_parts but now can be thrown for any related
        # client error.
        except ClientError as e:
            raise S3UploadFailedError(
                f"Failed to upload {filename} to {bucket}/{key}: {e}"
            )

    def download_file(
        self, bucket, key, filename, extra_args=None, callback=None

View on GitHub (pinned to c7b4afac23)

Solutions

  1. Pass the filesystem path as a string or pathlib.Path: s3.upload_file('/tmp/data.bin', bucket, key).
  2. If you have an open file handle, switch to s3.upload_fileobj(f, bucket, key).
  3. If the path comes from another call, assert it is set and is a str/Path before calling: isinstance(filename, (str, pathlib.Path)).

Example fix

// before
with open('/tmp/data.bin', 'rb') as f:
    s3.upload_file(f, 'bkt', 'key')  # wrong: file handle, not path

// after
s3.upload_file('/tmp/data.bin', 'bkt', 'key')
# or, for a file handle:
with open('/tmp/data.bin', 'rb') as f:
    s3.upload_fileobj(f, 'bkt', 'key')
Defensive patterns

Strategy: type-guard

Validate before calling

import os, pathlib
if not isinstance(filename, (str, os.PathLike)):
    raise TypeError('filename must be a str or os.PathLike')
s3.upload_file(filename, bucket, key)

Type guard

def is_upload_path(filename) -> bool:
    import os
    return isinstance(filename, (str, os.PathLike))

Try / catch

try:
    s3.upload_file(filename, bucket, key)
except ValueError as e:
    if 'string or a path-like' in str(e):
        s3.upload_fileobj(filename, bucket, key)  # filename was actually a file handle

Prevention

When it happens

Trigger: Calling upload_file(filename, ...) where filename is None, an int, an already-open file handle, a bytes object, or any non-str/non-PathLike value. Commonly: passing an open file where upload_fileobj was intended.

Common situations: Mixing up upload_file (path) vs upload_fileobj (file handle); passing None because the path variable was never assigned; passing a Path subclass that does not implement __fspath__.

Related errors


AI-assisted analysis of boto/boto3@c7b4afac23 (2026-08-04). Data as JSON: /data/errors/d50d8348d26aef2d.json. Report an issue: GitHub.