boto/boto3 · error · ValueError

Fileobj must implement write

Error message

Fileobj must implement write

What it means

Raised by the injected `download_fileobj` helper before a download starts. boto3 writes incoming bytes by calling `write()` on the supplied `Fileobj`, so the target must be a writable binary stream. Passing bytes, a string, a path, or a read-only object is rejected up front. Use `download_file` when you want to write directly to a path.

Solutions

  1. If you want a file on disk by path, use `client.download_file(bucket, key, filename)` instead of `download_fileobj`.
  2. Pass an open writable binary handle: `with open(path, 'wb') as f: client.download_fileobj(bucket, key, f)`.
  3. For in-memory capture, use `from io import BytesIO; buf = BytesIO(); client.download_fileobj(bucket, key, buf)`.
  4. Ensure the destination handle is opened in write/append binary mode (`'wb'`/`'ab'`), not read mode.

Example fix

# before
client.download_fileobj('mybucket', 'key.txt', '/tmp/key.txt')  # path string has no .write

# after
with open('/tmp/key.txt', 'wb') as f:
    client.download_fileobj('mybucket', 'key.txt', f)
Defensive patterns

Strategy: validation

Validate before calling

def download_target(obj):
    assert hasattr(obj, 'write'), 'download_fileobj needs a writable file-like object'
    return obj

with open(path, 'wb') as f:
    client.download_fileobj(bucket, key, download_target(f))

Type guard

def is_writable_fileobj(o) -> bool:
    return hasattr(o, 'write') and callable(o.write)

Try / catch

try:
    client.download_fileobj(bucket, key, target)
except ValueError as e:
    if 'must implement write' in str(e):
        with open(target, 'wb') as f:
            client.download_fileobj(bucket, key, f)  # target was a path

Prevention

When it happens

Trigger: Calling `client.download_fileobj(Bucket=..., Key=..., Fileobj=<bytes|str|path|None>)` or `bucket.download_fileobj(key, <read-only-or-non-file-object>)`. Common when a variable intended to be an open file is actually a path string or None.

Common situations: Confusing `download_file` (takes a path) with `download_fileobj` (takes an open writable stream); passing a path string; passing a BytesIO opened/used incorrectly; forgetting to open the destination; passing a file opened in `'rb'` (read-only) mode.

Related errors


AI-assisted analysis of boto/boto3@6e10b029c1 (2026-08-11). Data as JSON: /api/errors/cb87edc34bdcb3a1. Report an issue: GitHub.

Appendix: source

Thrown at boto3/s3/inject.py:841

    :type Fileobj: a file-like object
    :param Fileobj: A file-like object to download into. At a minimum, it must
        implement the `write` method and must accept bytes.

    :type ExtraArgs: dict
    :param ExtraArgs: Extra arguments that may be passed to the
        client operation. For allowed download arguments see
        :py:attr:`boto3.s3.transfer.S3Transfer.ALLOWED_DOWNLOAD_ARGS`.

    :type Callback: function
    :param Callback: A method which takes a number of bytes transferred to
        be periodically called during the download.

    :type Config: boto3.s3.transfer.TransferConfig
    :param Config: The transfer configuration to be used when performing the
        download.
    """
    if not hasattr(Fileobj, 'write'):
        raise ValueError('Fileobj must implement write')

    subscribers = None
    if Callback is not None:
        subscribers = [ProgressCallbackInvoker(Callback)]

    config = Config
    if config is None:
        config = TransferConfig()

    new_config = python_copy.copy(config)
    disable_threading_if_append_mode(new_config, Fileobj)

    with create_transfer_manager(self, new_config) as manager:
        future = manager.download(
            bucket=Bucket,
            key=Key,
            fileobj=Fileobj,
            extra_args=ExtraArgs,

View on GitHub (pinned to 6e10b029c1)