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.

4 min readSume
All posts

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.

Fetch transport-error retry rules from the Deno pull request, read 2026-10-01
ErrorConnectionRetried by fetch
incomplete_message / ECONNRESETPooled (reused)Yes
incomplete_message / ECONNRESETFreshNo
HTTP/2 GOAWAY, REFUSED_STREAMAnyYes, 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

All Developers posts

Written by Sume