aiohttp 3.14.4 fixes truncated retried file bodies: Sume submits
aiohttp 3.14.4 fixed an internal retry that sent a cut-off body from a file object. Send a Sume JSON submit from bytes and keep one Idempotency-Key.

If you submit a Sume job with aiohttp, build the JSON body in memory and pass the bytes, and upgrade to aiohttp 3.14.4. That release fixed a bug where an internally retried request sent a truncated body when the request data was a file object. A Sume submit body is a few hundred bytes, so there is no reason to hand aiohttp an open file at all.
What 3.14.4 fixed
The aiohttp changelog (read 2026-10-10) dates 3.14.4 to 2026-10-04; the GitHub release page shows the same notes. The items that touch a client calling Sume are below.
| Item | Changelog wording | Reference |
|---|---|---|
| Retried file bodies | Fixed internally retried requests sending truncated bodies from file objects | #13329, #13330 |
| Upload progress | Added aiohttp.UploadTracker; ClientResponse.output_size and upload_complete are deprecated | #13579 |
| Connector | Fixed BaseConnector(keepalive_timeout=None) crashing on subsequent requests | #13756, #13757 |
Why a cut-off body is worse than an error
A JSON body that stops halfway is not valid JSON. The errors page lists 400 invalid_request for a body that is not valid, so the good outcome is a clean rejection. The risky case is a retry that you cannot see: your code made one call, the library made two, and you now have to reason about whether the first one created a job.
That is what Idempotency-Key is for. The jobs page says to send the same key on a retried submit so the retry returns the original job instead of billing a second one. Pair that with a body that cannot be half-read.
import asyncio
import json
import os
import aiohttp
URL = os.environ.get("SUME_URL", "https://api.sume.com/v1/videos")
async def main() -> None:
payload = {"model": "seedance-2.5", "prompt": "비 오는 서울 골목", "duration": 5}
body = json.dumps(payload, ensure_ascii=False).encode("utf-8") # bytes in memory
headers = {
"Authorization": f"Bearer {os.environ['SUME_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": os.environ["RUN_KEY"], # same key on every retry
}
async with aiohttp.ClientSession() as session:
async with session.post(URL, data=body, headers=headers) as resp:
job = await resp.json()
print(resp.status, job.get("id"), job.get("polling_url"))
asyncio.run(main())Notes on the sample
The request shape follows the video docs: model, prompt, and an integer duration, with the response carrying id and polling_url. I ran the script on aiohttp 3.14.4 against a local stub and it printed the status, job id, and polling URL.
- Encode once with
ensure_ascii=Falseand.encode("utf-8"). TheContent-Typeheader saysapplication/json, and the bytes are the same on a retry. - Read the key from the environment or from your job table. Do not generate a new UUID inside the retry loop, because a new key is a new paid job.
- Poll
polling_urlafterward. Do not submit again because a local timeout fired. - If you really must upload a large file elsewhere,
UploadTrackeris the new way to watch progress. Sume's generation endpoints take public HTTPS URLs for media, so a Sume submit does not need it.
Pin aiohttp>=3.14.4 in the service that talks to Sume. The fix is in the library, but the habit of sending bytes you built yourself works on every version.
Sources
Related posts
More in Developers
- A sensor for a Sume Format run: poll, back off, time out
Poll a Sume Format run with a doubling backoff up to 60 seconds and a hard deadline. Here is the loop, the terminal states, and a 45 minute limit.
- Arazzo 1.1 workflow for Sume: submit, poll, retry until terminal
Arazzo 1.1.0 can describe Sume's submit then poll sequence over its operationIds, with a 2 s retry action. The YAML, and what I could and could not check.
- Assert the Sume usage ledger in CI: canceled job debits zero
After canceling a queued Sume job, read GET /v1/usage?job_id= and assert summary.final is true and debited_usd_micros is 0. A short Python CI check.
- AsyncAPI 3.1 for your Sume webhook receiver: a 29-line spec
Describe the receiver Sume calls in AsyncAPI 3.1.0: one receive operation, two signature headers, three job events. Parsed with the AsyncAPI parser.
Written by Sume