Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto Apto para principiantes
#6,975 0 comentarios 0 reacciones 1 asignado Ver en GitHub

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
Área
api, cli

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

🐛 Bug supabase/cli
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
  1. supabase start --runtime native in a project with [experimental] stack = true.
  2. Put API_URL and PUBLISHABLE_KEY from supabase status --env in the environment.
  3. Save the script below as keepalive-race.mjs and run node keepalive-race.mjs (Node 24.21.0 here). The request after a six-second block fails with ECONNRESET; 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 on develop) creates the client-facing server with createServer(...) and sets no keepAliveTimeout, 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

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de supabase/cli

Todos los issues de supabase/cli

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.