decolua/9router · error

[mitm] http2 client error: ${e.message}

Error message

[mitm] http2 client error: ${e.message}

What it means

In passthroughHttp2, the HTTP/2 client session's TLS connection to the target host (port 442/443, ALPN h2, TLS verification disabled) emits an 'error'. The handler logs '[mitm] http2 client error: <message>', writes the error into the dump, returns 502 Bad Gateway to the caller, and resolves the passthrough promise.

Source

Thrown at src/mitm/server.js:201

    const lk = k.toLowerCase();
    if (lk === "host" || lk === "connection" || lk === "keep-alive" ||
        lk === "transfer-encoding" || lk === "upgrade" || lk === "proxy-connection") continue;
    h2Headers[lk] = v;
  }
  h2Headers[":method"] = req.method;
  h2Headers[":path"] = req.url;
  h2Headers[":scheme"] = "https";
  h2Headers[":authority"] = targetHost;

  return new Promise((resolve) => {
    const client = http2.connect(`https://${targetHost}`, {
      createConnection: () => tls.connect({
        host: targetIP, port: 443, servername: targetHost,
        ALPNProtocols: ["h2"], rejectUnauthorized: false,
      }),
    });
    client.once("error", (e) => {
      err(`[mitm] http2 client error: ${e.message}`);
      if (dumper) { dumper.writeChunk(`\n[ERROR h2] ${e.message}\n`); dumper.end(); }
      if (!res.headersSent) res.writeHead(502);
      if (!res.writableEnded) res.end("Bad Gateway");
      try { client.close(); } catch {}
      resolve();
    });

    const stream = client.request(h2Headers, { endStream: bodyBuffer.length === 0 });
    if (bodyBuffer.length > 0) stream.end(bodyBuffer);

    stream.once("response", (responseHeaders) => {
      const status = responseHeaders[":status"];
      // Filter pseudo-headers + connection-specific
      const outHeaders = {};
      for (const [k, v] of Object.entries(responseHeaders)) {
        if (k.startsWith(":")) continue;
        if (k === "connection" || k === "keep-alive" || k === "transfer-encoding") continue;
        outHeaders[k] = v;

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Read the underlying message (ENOTFOUND/ECONNREFUSED/ETIMEDOUT) to identify DNS vs connectivity.
  2. Verify the mitm DNS entries: run the dns cleanup (removeAllDNSEntriesSync equivalent) or check /etc/hosts points correctly.
  3. Test direct connectivity: curl -v https://<targetHost> to see if the upstream itself is down.
  4. Check firewall/VPN rules allowing outbound 443 to the provider.

Example fix

// before: stale hosts entry
127.0.0.1 api.anthropic.com   # h2 client gets ECONNREFUSED
// after
# entry removed / DNS restored
curl -v https://api.anthropic.com  # succeeds
Defensive patterns

Strategy: retry

Validate before calling

// pre-check reachability and DNS before passthrough
await dns.promises.lookup(targetHost); // throws ENOTFOUND early
const alive = await fetch(`https://${targetHost}`, { method: 'HEAD' }).then(r => true).catch(() => false);
if (!alive) console.warn(`Upstream ${targetHost} unreachable — expect 502`);

Try / catch

client.once('error', (e) => {
  err(`[mitm] http2 client error: ${e.message}`);
  if (isRetryable(e) && attempts < 2) return retryWithBackoff();
  if (!res.headersSent) res.writeHead(502);
  if (!res.writableEnded) res.end('Bad Gateway');
  try { client.close(); } catch {}
  resolve();
});

Prevention

When it happens

Trigger: tls.connect to targetIP:443 fails — DNS resolution failure of targetHost, connection refused/timeout, certificate or ALPN negotiation error, or the socket is reset mid-session.

Common situations: MITM DNS override removed but hosts file still redirects the domain to 127.0.0.1; target domain unreachable/blocked by firewall or VPN; upstream momentarily down; IPv6/IPv4 targetIP stale in the DNS cache entries the MITM wrote.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/b7f7817c0a98bab1. Report an issue: GitHub.