boto/boto3 · error · ValueError
Fileobj must implement read
Error message
Fileobj must implement read
What it means
Raised by the injected `upload_fileobj` helper before an upload starts. boto3 streams bytes by calling `read()` on the supplied `Fileobj`, so it requires a genuine file-like object. Passing bytes, a string, a path, or any object lacking a `read` method is rejected immediately rather than failing mid-transfer. Use `upload_file` (not `upload_fileobj`) when you only have a filename.
Solutions
- If you have a path, switch to `client.upload_file(filename, bucket, key)` instead of `upload_fileobj`.
- If you have in-memory bytes, wrap them: `from io import BytesIO; client.upload_fileobj(BytesIO(data), bucket, key)`.
- If using a real file, open it in binary mode and pass the handle: `with open(path, 'rb') as f: client.upload_fileobj(f, bucket, key)`.
- Verify the object is not None before the call: assert the variable holding the body is assigned.
Example fix
# before client.upload_fileobj(b'hello world', 'mybucket', 'key.txt') # bytes has no .read # after from io import BytesIO client.upload_fileobj(BytesIO(b'hello world'), 'mybucket', 'key.txt')
Defensive patterns
Strategy: validation
Validate before calling
def upload_body(obj):
assert hasattr(obj, 'read'), 'upload_fileobj needs a file-like object with read()'
return obj
client.upload_fileobj(upload_body(body), bucket, key) Type guard
from io import IOBase
def is_readable_fileobj(o) -> bool:
return hasattr(o, 'read') and callable(o.read) Try / catch
try:
client.upload_fileobj(body, bucket, key)
except ValueError as e:
if 'must implement read' in str(e):
from io import BytesIO
client.upload_fileobj(BytesIO(bytes(body)), bucket, key) # or upload_file with a path Prevention
- Always open files in binary mode ('rb') for uploads.
- Use upload_file for paths and upload_fileobj for streams.
- Wrap raw bytes in BytesIO before passing to upload_fileobj.
When it happens
Trigger: Calling `client.upload_fileobj(Bucket=..., Key=..., Body=<bytes|str|None|dict>)`, `bucket.upload_fileobj(<bytes>, key)`, or `s3.upload_fileobj(fileobj=<object-without-read>)`. Also triggered by an empty/None body variable that was expected to be an open file handle.
Common situations: Confusing `upload_file` (takes a path) with `upload_fileobj` (takes an open binary stream); passing `open(path)` in text mode `'r'` (this actually has `read` but corrupts binary data, and some wrappers still fail); forgetting to open the file; passing `b'data'` directly.
Related errors
- Filename must be a string or a path-like object
- Fileobj must implement write
- Failed to upload to /
- CRT transfer client is configured but is missing minimum…
- Either a boto3.Client or s3transfer.manager.TransferManager…
AI-assisted analysis of boto/boto3@6e10b029c1 (2026-08-11).
Data as JSON: /api/errors/194f6b08dab5d29e.
Report an issue: GitHub.
Appendix: source
Thrown at boto3/s3/inject.py:660
:type Key: str
:param Key: The name of the key to upload to.
:type ExtraArgs: dict
:param ExtraArgs: Extra arguments that may be passed to the
client operation. For allowed upload arguments see
:py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS`.
:type Callback: function
:param Callback: A method which takes a number of bytes transferred to
be periodically called during the upload.
:type Config: boto3.s3.transfer.TransferConfig
:param Config: The transfer configuration to be used when performing the
upload.
"""
if not hasattr(Fileobj, 'read'):
raise ValueError('Fileobj must implement read')
subscribers = None
if Callback is not None:
subscribers = [ProgressCallbackInvoker(Callback)]
config = Config
if config is None:
config = TransferConfig()
with create_transfer_manager(self, config) as manager:
future = manager.upload(
fileobj=Fileobj,
bucket=Bucket,
key=Key,
extra_args=ExtraArgs,
subscribers=subscribers,
)
return future.result()View on GitHub (pinned to 6e10b029c1)