Deno fetch stops retrying fresh-connection POSTs: Sume keys
Deno now retries fetch transport errors only on reused connections. That cuts duplicate POSTs, but a paid Sume submit still needs an Idempotency-Key.

A silent retry of a POST is how one request becomes two. Deno merged a fix on August 12 that narrows when fetch retries on its own, but the safe answer for a paid Sume submit does not depend on the runtime: send an Idempotency-Key.
Deno facts are from the merged pull request; Sume facts from Communication modes, read 2026-10-01. The page states no release version, so check which Deno build includes it.
What did the Deno change do?
The pull request, which fixes issue 35610, makes fetch retry incomplete_message and ECONNRESET transport errors only on pooled, reused connections. HTTP/2 GOAWAY and REFUSED_STREAM stay retried unconditionally. The stated reason is that an error on a fresh connection likely means the server did process the request, so retrying risks duplicating a POST. The issue was seen on Deno 2.9.0, where Deno sent a request twice that Node 26.3.0 sent once.
| Error | Connection | Retried by fetch |
|---|---|---|
incomplete_message / ECONNRESET | Pooled (reused) | Yes |
incomplete_message / ECONNRESET | Fresh | No |
| HTTP/2 GOAWAY, REFUSED_STREAM | Any | Yes, unconditionally |
Does that make a Sume submit safe on its own?
No. The remaining retry cases still replay the request, your own code or a proxy may retry too, and a lost response looks identical to a failed request. Sume answers a submit with 202 and the job envelope, so a retried duplicate that is not recognized is a second paid job.
What should the submit send?
Generate one key per logical submit, outside any retry loop, and put it in the Idempotency-Key header. An exact retry then returns the original job. Reuse a key only for the same operation and payload; anything else is 409 idempotency_conflict.
const key = crypto.randomUUID(); // once per logical submit
export async function submit(prompt: string): Promise<Response> {
return await fetch("https://api.sume.com/v1/video-1.0/generate", {
method: "POST",
headers: {
"x-api-key": Deno.env.get("SUME_API_KEY") ?? "",
"content-type": "application/json",
"idempotency-key": key,
},
body: JSON.stringify({ prompt, mode: "async" }),
});
}What about 429 and 503?
Those are answers, not transport errors, so fetch does not retry them. Read retry-after on a 429 rate_limited, which also names error.details.scope as read or write. For a rejected submit, check the error's retryable field before looping.
Sources
Related posts
More in Developers
- Remove video speckle noise by API: median filter radius
Sume's video filter allowlists median, which replaces each pixel with the middle value of its neighbours. Radius runs 1 to 127; start at 1 and compare.
- Extract audio from MP4 to WAV or MP3: the detach defaults
Sume audio detach returns WAV (pcm_s16le, sample-exact) by default or MP3 at 128 kbps. Pick WAV for speech-to-text and timeline use, MP3 when file size matters.
- Extract audio segment start beyond video length: error and fix
Audio detach fails detach_start_past_source when range.start is past the probed duration. It differs from audio_detach_range_empty. Check duration first.
- Devin Desktop ACP session/new mcp_servers with Sume's remote URL
Devin Desktop 3.10.35 uses MCP servers an ACP client passes in session/new. Pass Sume's streamable HTTP URL and an API-key header, not editor config.
Written by Sume