Bun 1.4.2 fixes an AsyncLocalStorage leak: per-request Sume keys
Bun 1.4.2 fixes an AsyncLocalStorage leak from 1.4.1. Carry a per-request Sume Idempotency-Key in the store, and check your Bun version first.

If your Bun server keeps a Sume Idempotency-Key in AsyncLocalStorage, upgrade to Bun 1.4.2 or later before you rely on it. The Bun v1.4.2 release notes describe a fix for an AsyncLocalStorage memory leak regression introduced in 1.4.1: a timer, immediate or promise created inside store.exit() or a nested store.run() kept the outer store alive. A store that holds per-request data and never gets released is a slow leak on a busy API.
The pattern itself is simple. Put the key for one incoming order into the store at the edge of the request, and let the function that calls Sume read it. That keeps the key out of every function signature, and every retry inside the request reuses the same value.
Why keep the key in the request context
Sume's rule is that the same key with the same payload returns the original job, while a different payload under the same key returns a 409, as the jobs and results docs explain. So the key should be a function of the order, not of the attempt. Deriving it once at the edge, for example order-<id>-v1, makes a retried HTTP request, a retried queue message and a retried function all land on the same job.
Fetching it from the store inside the submit function has a second benefit: a call made outside any request context throws instead of silently sending no key. A paid submit without a key cannot be deduplicated.
| Fact | Detail | Source |
|---|---|---|
| Bun 1.4.2 release date | Sep 5, 2026 | Bun release notes |
| Regression fixed | AsyncLocalStorage leak introduced in 1.4.1 | Bun release notes |
| Trigger | Timer, immediate or promise created in store.exit() or nested store.run() | Bun release notes |
| Sume key rule | Same key and payload returns the original job | Sume jobs docs |
Steps
- Check
bun --versionin CI and fail the build below 1.4.2 if you useAsyncLocalStorageon a long-running server. - Derive the key from a stable order id and a payload version at the entry point, then call
als.run({ key }, handler). - Read the key in the one function that calls Sume. Throw when it is missing.
- Bump the version suffix, for example
-v2, only when the prompt or settings change on purpose.
Sample
This server derives a key from the order query parameter and submits an async image job. It was run on Bun 1.4.0 against a local stand-in for the API, and two concurrent requests kept separate keys. Set SUME_API_KEY to try it against the real API.
import { AsyncLocalStorage } from "node:async_hooks";
const base = process.env.SUME_BASE ?? "https://api.sume.com";
const als = new AsyncLocalStorage();
async function submitImage(prompt) {
const key = als.getStore()?.key;
if (!key) throw new Error("no Idempotency-Key in this request context");
const res = await fetch(`${base}/v1/images`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": key,
},
body: JSON.stringify({ model: "sume/auto", prompt, mode: "async" }),
});
return { status: res.status, key };
}
const server = Bun.serve({
port: 0,
fetch(req) {
const order = new URL(req.url).searchParams.get("order") ?? "none";
return als.run({ key: `order-${order}-v1` }, async () => {
await new Promise((r) => setTimeout(r, 5));
return Response.json(await submitImage("studio shot of a red kettle"));
});
},
});
const out = await Promise.all(["a1", "b2"].map((o) => fetch(`${server.url}?order=${o}`).then((r) => r.json())));
console.log(out);
server.stop();What Sume does not do
Sume does not look at your server's request context and cannot make a key for you. If you use the TypeScript SDK, note that its client retries a POST only when an Idempotency-Key is present. Sume also does not tell you which Bun version leaks; that comes from the Bun release notes, so verify against your own build.
Sources
Related posts
More in Developers
- bun test for a Sume webhook verifier: five cases to run on Bun 1.4.2
Five bun:test cases for a Sume webhook verifier: fresh, tampered, stale, rotation and an empty secret, plus a pin to Bun 1.4.2 or later. Run on Bun 1.4.0.
- Lost video callback? Sweep pending jobs after the 10-attempt window
Sume retries a job webhook 10 times, 30 seconds apart. If none gets through, the job is still done. A 30-line sweeper that polls pending jobs recovers it.
- callback_url on /v1/videos: the Sume job envelope that arrives
A /v1/videos callback_url delivers Sume's job.completed, job.failed or job.canceled envelope, not video.generation.* events. Payload, signature, checks.
- callback_url or webhook_url? Which Sume route takes which field name
POST /v1/videos takes callback_url; the model endpoints take mode webhook with webhook_url. Both must be public HTTPS and both deliver signed job events.
Written by Sume