{"record":{"id":"b7f7817c0a98bab1","repo":"decolua/9router","slug":"mitm-http2-client-error-e-message","errorCode":null,"errorMessage":"[mitm] http2 client error: ${e.message}","messagePattern":"\\[mitm\\] http2 client error: (.+?)","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"src/mitm/server.js","lineNumber":201,"sourceCode":"    const lk = k.toLowerCase();\n    if (lk === \"host\" || lk === \"connection\" || lk === \"keep-alive\" ||\n        lk === \"transfer-encoding\" || lk === \"upgrade\" || lk === \"proxy-connection\") continue;\n    h2Headers[lk] = v;\n  }\n  h2Headers[\":method\"] = req.method;\n  h2Headers[\":path\"] = req.url;\n  h2Headers[\":scheme\"] = \"https\";\n  h2Headers[\":authority\"] = targetHost;\n\n  return new Promise((resolve) => {\n    const client = http2.connect(`https://${targetHost}`, {\n      createConnection: () => tls.connect({\n        host: targetIP, port: 443, servername: targetHost,\n        ALPNProtocols: [\"h2\"], rejectUnauthorized: false,\n      }),\n    });\n    client.once(\"error\", (e) => {\n      err(`[mitm] http2 client error: ${e.message}`);\n      if (dumper) { dumper.writeChunk(`\\n[ERROR h2] ${e.message}\\n`); dumper.end(); }\n      if (!res.headersSent) res.writeHead(502);\n      if (!res.writableEnded) res.end(\"Bad Gateway\");\n      try { client.close(); } catch {}\n      resolve();\n    });\n\n    const stream = client.request(h2Headers, { endStream: bodyBuffer.length === 0 });\n    if (bodyBuffer.length > 0) stream.end(bodyBuffer);\n\n    stream.once(\"response\", (responseHeaders) => {\n      const status = responseHeaders[\":status\"];\n      // Filter pseudo-headers + connection-specific\n      const outHeaders = {};\n      for (const [k, v] of Object.entries(responseHeaders)) {\n        if (k.startsWith(\":\")) continue;\n        if (k === \"connection\" || k === \"keep-alive\" || k === \"transfer-encoding\") continue;\n        outHeaders[k] = v;","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/mitm/server.js#L183-L219","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: stale hosts entry\n127.0.0.1 api.anthropic.com   # h2 client gets ECONNREFUSED\n// after\n# entry removed / DNS restored\ncurl -v https://api.anthropic.com  # succeeds","handlingStrategy":"retry","validationCode":"// pre-check reachability and DNS before passthrough\nawait dns.promises.lookup(targetHost); // throws ENOTFOUND early\nconst alive = await fetch(`https://${targetHost}`, { method: 'HEAD' }).then(r => true).catch(() => false);\nif (!alive) console.warn(`Upstream ${targetHost} unreachable — expect 502`);","typeGuard":null,"tryCatchPattern":"client.once('error', (e) => {\n  err(`[mitm] http2 client error: ${e.message}`);\n  if (isRetryable(e) && attempts < 2) return retryWithBackoff();\n  if (!res.headersSent) res.writeHead(502);\n  if (!res.writableEnded) res.end('Bad Gateway');\n  try { client.close(); } catch {}\n  resolve();\n});","preventionTips":["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."],"tags":["http2","tls","mitm","network"],"backgroundTag":"connection-refused","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}