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
- Read the underlying message (ENOTFOUND/ECONNREFUSED/ETIMEDOUT) to identify DNS vs connectivity.
- Verify the mitm DNS entries: run the dns cleanup (removeAllDNSEntriesSync equivalent) or check /etc/hosts points correctly.
- Test direct connectivity: curl -v https://<targetHost> to see if the upstream itself is down.
- 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
- Keep MITM DNS/hosts overrides in sync — clean entries when stopping the proxy.
- Monitor upstream provider status pages for outages.
- Verify VPN/firewall allows outbound 443 to AI providers.
- Prefer passthrough over interception for hosts known to reject h2 MITM.
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
- Cursor AgentService request failed: ${error.message}
- [mitm] http2 stream error: ${e.message}
- Sudo password required to install Root CA certificate
- Failed to trust certificate: ${e.message}
- Machine ID is required for Cursor API
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/b7f7817c0a98bab1.
Report an issue: GitHub.