Description
AsyncClient.send_images() documents that at most four images may be supplied.
However, the method starts an upload_blob() operation for every item in images using asyncio.gather() before the four-image constraint is enforced by the embed model.
As a result, an oversized input can trigger upload work for every supplied image even though the resulting image embed cannot contain more than four images.
I reproduced the upload-admission behavior with controlled local upload doubles. With 512 input images, all 512 upload operations were admitted before any upload was allowed to complete.
Observed behavior:
Input images: 512
Uploads admitted before any completed: 512
Peak concurrent uploads: 512
External service contacted: No
Rejecting an over-limit image list before starting uploads would avoid unnecessary concurrent upload operations and network side effects for an input that cannot produce a valid four-image embed.
Affected Version
Confirmed on:
atproto==0.0.69
Python 3.10.12
packages/atproto_client/client/async_client.py:196-257
Other versions have not been tested.
Reproduction
The attached PoC executes the released AsyncClient.send_images() implementation with 512 local image inputs.
upload_blob() is replaced with a local asynchronous double that records when each upload starts and waits on a shared gate. The gate is released only after every upload operation has entered the double.
Observed result:
input images: 512
uploads admitted before release: 512
peak concurrent uploads: 512
unbounded fan-out reproduced: yes
No Bluesky service or other network endpoint is contacted during reproduction.
Expected behavior
send_images() should reject an image list containing more than four items before invoking upload_blob() for any of them.
Attachment
run.py
images.json
Description
AsyncClient.send_images() documents that at most four images may be supplied.
However, the method starts an upload_blob() operation for every item in images using asyncio.gather() before the four-image constraint is enforced by the embed model.
As a result, an oversized input can trigger upload work for every supplied image even though the resulting image embed cannot contain more than four images.
I reproduced the upload-admission behavior with controlled local upload doubles. With 512 input images, all 512 upload operations were admitted before any upload was allowed to complete.
Observed behavior:
Input images: 512
Uploads admitted before any completed: 512
Peak concurrent uploads: 512
External service contacted: No
Rejecting an over-limit image list before starting uploads would avoid unnecessary concurrent upload operations and network side effects for an input that cannot produce a valid four-image embed.
Affected Version
Confirmed on:
atproto==0.0.69
Python 3.10.12
packages/atproto_client/client/async_client.py:196-257
Other versions have not been tested.
Reproduction
The attached PoC executes the released AsyncClient.send_images() implementation with 512 local image inputs.
upload_blob() is replaced with a local asynchronous double that records when each upload starts and waits on a shared gate. The gate is released only after every upload operation has entered the double.
Observed result:
input images: 512
uploads admitted before release: 512
peak concurrent uploads: 512
unbounded fan-out reproduced: yes
No Bluesky service or other network endpoint is contacted during reproduction.
Expected behavior
send_images() should reject an image list containing more than four items before invoking upload_blob() for any of them.
Attachment
run.py
images.json