stack: the HTTP gateway closes idle keep-alive connections after 5 s, so a client whose event loop is blocked gets ECONNRESET (`fetch failed`) on its next request
Los mantenedores suelen responder en 1 día
@7ttp ya está trabajando en esto.
Desde el 3/10/2026.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 84/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- node.js, typescript
Línea de trabajo
Empieza por packages/stack/src/HttpProxy.ts, especialmente por createServer(...) y el comentario existente sobre el timeout del Agent upstream. Ejecuta la reproducción keepalive-race.mjs proporcionada contra el stack nativo y confirma que un bloqueo del bucle de eventos del cliente de más de cinco segundos ya no reinicia la siguiente solicitud. El timeout de keep-alive orientado al cliente debe ser superior a cinco segundos, manteniendo headersTimeout por encima de este.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Affected area
Local development
Supabase CLI version
2.119.0
Operating system
macOS 26.7 on Apple silicon, native runtime. First seen on a GitHub Actions ubuntu-latest runner (x64), native runtime.
Installation method
pnpm
Command
supabase start --runtime native # [experimental] stack = true in config.toml
Actual output
The stack's HTTP gateway (the listener behind API_URL) advertises a five-second idle timeout on every response and closes an idle client connection after it:
$ curl -sI "$API_URL/rest/v1/" -H "apikey: $PUBLISHABLE_KEY"
HTTP/1.1 200 OK
server: postgrest/16.4
Connection: keep-alive
Keep-Alive: timeout=5
A Node client whose event loop is free is not affected: fetch's connection pool drops the idle socket in time, and sequential requests seconds apart all succeed. A client whose event loop is blocked for longer than five seconds between two requests on the same connection (a synchronous child process, a long GC pause) cannot do that; the gateway closes the socket first, the client writes its next request to it, and the request fails:
warm: 200
after 4 s with the event loop blocked: 200
warm: 200
after 6 s with the event loop blocked: fetch failed (ECONNRESET)
warm: 200
after 6 s with the event loop free: 200
Through supabase-js this surfaces as TypeError: fetch failed, with nothing about the cause. It first hit us in CI: an end-to-end seed called the Data API, ran two builds with execFileSync, and called the Data API again. It passed on a laptop, where the two builds took less than five seconds, and failed on an ubuntu-latest runner. The same seed had run unchanged against the Docker stack (Kong) for weeks.
Expected behavior
The gateway keeps an idle client connection long enough that a stall of a few seconds in a client does not become a connection reset, as the Docker stack's gateway did: an explicit keepAliveTimeout well above the runtime's five-second default, with headersTimeout kept above it.
Steps to reproduce
supabase start --runtime nativein a project with[experimental] stack = true.- Put
API_URLandPUBLISHABLE_KEYfromsupabase status --envin the environment. - Save the script below as
keepalive-race.mjsand runnode keepalive-race.mjs(Node 24.21.0 here). The request after a six-second block fails withECONNRESET; the same gap with the loop free succeeds.
import { execFileSync } from "node:child_process";
const url = `${process.env.API_URL}/rest/v1/`;
const headers = { apikey: process.env.PUBLISHABLE_KEY };
async function get(label) {
try {
const response = await fetch(url, { headers });
await response.text();
console.log(`${label}: ${response.status}`);
} catch (error) {
console.log(`${label}: ${error.message} (${error.cause?.code})`);
}
}
for (const seconds of [4, 6]) {
await get("warm");
execFileSync("sleep", [String(seconds)]); // blocks the event loop, as any synchronous work does
await get(`after ${seconds} s with the event loop blocked`);
}
await get("warm");
await new Promise((resolve) => setTimeout(resolve, 6000)); // the same gap with the loop free
await get("after 6 s with the event loop free");
Additional context
packages/stack/src/HttpProxy.ts(v2.119.0, unchanged ondevelop) creates the client-facing server withcreateServer(...)and sets nokeepAliveTimeout, so the runtime's default of five seconds applies. The same file already guards the gateway's own upstream connections against this race:new Agent({ keepAlive: true, timeout: 4_000 }), with the comment "Idle sockets close before Node upstreams' default 5 s keep-alive timeout can race a reuse." The client-facing side has no equivalent.- #6922 covered the upstream half of the gateway's connection handling; this is the client-facing half.
- We fixed our side by not blocking the event loop between requests, which is the right shape regardless, so this is a report rather than a blocker.
- Lenguaje dominante
- TypeScript
- Estrellas
- 2.4k
- Forks
- 533
- Merge medio
- 1 d 3 h
- PR fusionados (30 d)
- 322
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de supabase/cli
-
🐛 Bug supabase/cli
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
db schema declarative sync: no way to fail (non-zero exit) when the generated migration is destructiveQuizá libre de nuevo Un pull request para esta issue se cerró sin fusionarse. Abierto✨ Feature supabase/cli
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 1 día
-
📘 Docs supabase/cli
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
supabase/cli#6974 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Migration error caret is missing or misplaced when the statement contains multibyte charactersAbierto🐛 Bug supabase/cli
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
-
📘 Docs supabase/cli
Dificultad 4/5 1-2 días Aptitud para principiantes 42/100
Los mantenedores suelen responder en 1 día
Todos los issues de supabase/cli
Issues similares
-
status: needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
a2ui-project/a2ui#3158 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
hemilabs/ui-monorepo#2475 ·
Los mantenedores suelen responder en 1 día
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Dificultad 1/5 Menos de una hora Aptitud para principiantes 82/100
lingdojo/kana-dojo#32279 · 1 comentario · 5 reacciones ·
Los mantenedores suelen responder en 1 día
-
code-quality refactoring
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
github/gh-aw-firewall#9879 ·
Los mantenedores suelen responder en 1 día
-
[Bug]: Arrow-key cursor movement in combined diff view doesn't scroll to keep the cursor visibleAbiertoos:macos
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
Los mantenedores suelen responder en 1 día