Supabase Edge Function 504 at 150 s idle: Sume sync stays under it

A Supabase Edge Function that sends no response in 150 seconds returns 504. Sume sync waits at most 30 seconds: a bounded wait, then poll.

5 min readSume
All posts

Supabase returns a 504 Gateway Timeout when an Edge Function sends no response within a 150-second request idle timeout, a separate limit from the wall-clock cap of 150 seconds on Free and 400 seconds on Paid. Sume's sync mode waits at most 30 seconds, so it fits inside both, but it is a bounded wait: if the job is not done you get the job id back and poll.

Which Supabase limit produces the 504?

Supabase's limits page lists the request idle timeout as 150 seconds and says that if an Edge Function does not send a response before the timeout, a 504 Gateway Timeout is returned. Wall-clock duration is how long the worker stays active: 150 seconds on Free and 400 seconds on Paid. CPU time is 2 seconds per request and does not count async I/O, so waiting on a network call is cheap in CPU terms.

Because async waiting does not count as CPU, the danger is not your compute budget. It is the clock.

Supabase Edge Function limits (read 2026-10-02)
LimitValue
Request idle timeout150 s, then 504
Wall-clock duration150 s Free, 400 s Paid
CPU time per request2 s, excluding async I/O
Memory256 MB
Function size20 MB via CLI, 5 MB server-side bundling

How long does a Sume call actually block?

That depends on the mode. With async, the submit returns 202 with the job envelope right away. With sync (and its alias subscribe), the call waits for a terminal transition for up to wait_timeout_seconds, clamped to 0 to 30. The jobs page adds that the wait can be shorter when waiter capacity is unavailable.

Thirty seconds is a fifth of the 150-second idle limit, so a single sync call is safe. The risk is the loop around it: a function that retries the sync call three times in a row, or polls with sleeps, can walk past 150 seconds with no response and get cut.

What should the function return, and when?

For a quick image, call sync with a short wait and return the result if the envelope says terminal. If not, return the job id and let the browser or a second function poll. Never resubmit the paid request because the wait expired: Sume's docs say to poll, and a retry of the submit must reuse the same Idempotency-Key.

For anything longer, use mode: "webhook" and answer immediately. The signed job.completed POST then lands in a separate function.

Deno.serve(async (req) => {
  const { orderId, prompt } = await req.json();
  const res = await fetch("https://api.sume.com/v1/image-1.0/generate", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${Deno.env.get("SUME_API_KEY")}`,
      "Content-Type": "application/json",
      "Idempotency-Key": `order-${orderId}-v1`,
    },
    body: JSON.stringify({ prompt, mode: "sync", wait_timeout_seconds: 20 }),
    signal: AbortSignal.timeout(40_000),
  });
  const job = await res.json();
  // job.terminal and job.result_ready say whether to read the result now
  return Response.json(job, { status: res.ok ? 200 : res.status });
});

What if the wait does not finish?

Read the envelope. If it is not terminal, it carries status_url, result_url and, when present, next_poll_after_seconds. Poll status_url honoring that value, or back off exponentially, then fetch the result once result_ready is true.

Sume's envelope also has a sync object: sync.timed_out is true when the wait returned before a terminal state, and sync.capacity_exhausted is true when the wait was skipped. Both are normal and both are 2xx.

What does Sume not do?

Sume does not stream progress to your function. There is no SSE or WebSocket transport on the Developer API today, and GET /v1/jobs/:id/events is a snapshot, not a stream. Sync is a bounded wait and nothing more.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume