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 a `PathLike`-to-string conversion attempt: the `filename` argument must ultimately be a `str`. boto3 accepts `os.PathLike` objects (it calls `os.fspath` on them) but rejects anything else (None, int, bytes, an already-open file object). To stream from an open file handle, use `upload_fileobj` instead.
Solutions
- Pass a real filesystem path as a string or pathlib.Path: `client.upload_file('/tmp/f', bucket, key)`.
- If you have an open file object, switch to `client.upload_fileobj(fileobj, bucket, key)`.
- Guard against None: `if filename: client.upload_file(filename, ...)`.
- Coerce pathlib Paths explicitly if unsure: `str(pathlib.Path(...))`.
Example fix
# before
with open('/tmp/f', 'rb') as f:
client.upload_file(f, 'bucket', 'key') # f is a file object, not a path
# after
client.upload_file('/tmp/f', 'bucket', 'key')
# or, for an open handle:
with open('/tmp/f', 'rb') as f:
client.upload_fileobj(f, 'bucket', 'key') Defensive patterns
Strategy: type-guard
Validate before calling
from pathlib import Path
import os
def upload_path(p):
if isinstance(p, os.PathLike):
p = os.fspath(p)
if not isinstance(p, str):
raise TypeError('filename must be a str or os.PathLike')
client.upload_file(p, bucket, key) Type guard
import os
def is_path_or_str(p) -> bool:
return isinstance(p, (str, os.PathLike)) Try / catch
try:
client.upload_file(filename, bucket, key)
except ValueError as e:
if 'Filename must be a string' in str(e):
client.upload_file(str(filename), bucket, key) # or use upload_fileobj Prevention
- Use upload_file only for filesystem paths; use upload_fileobj for open handles.
- Build paths with pathlib.Path to stay PathLike-safe.
- Reject None filenames at your input boundary.
When it happens
Trigger: Calling `client.upload_file(None, bucket, key)`, `upload_file(b'data', ...)`, `upload_file(open(path), ...)` (passing a file object), or `upload_file(123, ...)`.
Common situations: Passing an open file handle to `upload_file` (meant for `upload_fileobj`); a filename variable that resolved to None due to a missing user input or a failed `tempfile` call; building the path with bytes on a system where `os.fspath` does not yield `str`.
Related errors
- Fileobj must implement read
- Either a boto3.Client or s3transfer.manager.TransferManager…
- Failed to upload to /
- Fileobj must implement write
- Manager cannot be provided with client, config, nor osutil…
AI-assisted analysis of boto/boto3@6e10b029c1 (2026-08-11).
Data as JSON: /api/errors/d50d8348d26aef2d.
Report an issue: GitHub.
Appendix: 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=NoneView on GitHub (pinned to 6e10b029c1)