windmill-labs/windmill · error

Bad Gateway

Error message

Bad Gateway

What it means

The dev proxy in cli/src/commands/dev/dev.ts forwards each client request to an upstream server. When the outbound proxyReq emits an 'error' (target unreachable, connection reset, TLS failure), the handler logs the cause and responds to the client with HTTP 502 and the body 'Bad Gateway'. This is the proxy signaling that the upstream failed, not the client request itself.

Source

Thrown at cli/src/commands/dev/dev.ts:873

        path: clientReq.url,
        method: clientReq.method,
        headers: fwdHeaders,
      };

      const proxyReq = httpModule.request(proxyOpts, (proxyRes) => {
        const setCookie = proxyRes.headers["set-cookie"];
        if (setCookie) {
          proxyRes.headers["set-cookie"] = setCookie.map((cookie) =>
            cookie.replace(/domain=[^;]+/gi, "domain=localhost")
          );
        }
        clientRes.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
        proxyRes.pipe(clientRes, { end: true });
      });

      proxyReq.on("error", (err) => {
        console.error("Proxy error:", err.message);
        clientRes.writeHead(502);
        clientRes.end("Bad Gateway");
      });

      clientReq.pipe(proxyReq, { end: true });
    });

    // WebSocket upgrades
    proxyServer.on("upgrade", (req, socket, head) => {
      const pathname = req.url?.split("?")[0] ?? "";

      if (pathname === "/ws_dev" || pathname === "/ws") {
        devWss.handleUpgrade(req, socket, head, (ws) => {
          devWss.emit("connection", ws, req);
        });
        return;
      }

      if (pathname.startsWith("/ws/") || pathname.startsWith("/ws_mp/") || pathname.startsWith("/ws_debug/")) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Check the terminal running the dev command for the 'Proxy error:' line — the err.message names the real cause
  2. Verify the upstream server (backend) is actually running and listening on the configured port
  3. Confirm the REMOTE/target port env var matches the port the backend binds to
  4. Restart the dev command after the upstream is healthy; retry the request

Example fix

// before
cargo run  # not started; proxy calls fail
// after
cd backend && cargo run  # wait for 'listening', then run dev frontend
Defensive patterns

Strategy: retry

Validate before calling

// before issuing requests through the dev proxy, check the upstream is reachable
const upstream = process.env.REMOTE ?? 'http://localhost:8000';
await fetch(upstream + '/api/version').catch(() => { throw new Error(`upstream ${upstream} not reachable — start the backend first`); });

Try / catch

// on the client side of the proxy
try {
  const res = await fetch(url);
  if (res.status === 502) throw new Error('dev proxy upstream failed — is the backend running?');
} catch (err) { /* surface upstream health message */ }

Prevention

When it happens

Trigger: Any request proxied to the upstream where the socket errors: upstream port not listening (backend not started or crashed), connection refused/reset, DNS failure, or TLS handshake failure on the target the dev command proxies to.

Common situations: Running the dev frontend before the backend is up or after the backend died; wrong REMOTE/port env var; backend restarted mid-request; firewall or VPN blocking the upstream port.

Understand the failure class

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/479dc3aa701e19a5. Report an issue: GitHub.