{"record":{"id":"26b2b69201c41e47","repo":"decolua/9router","slug":"http2-module-not-available","errorCode":null,"errorMessage":"http2 module not available","messagePattern":"http2 module not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/executors/cursor.js","lineNumber":327,"sourceCode":"\n  async makeFetchRequest(url, headers, body, signal, proxyOptions = null) {\n    const response = await proxyAwareFetch(url, {\n      method: \"POST\",\n      headers,\n      body,\n      signal\n    }, proxyOptions);\n\n    return {\n      status: response.status,\n      headers: Object.fromEntries(response.headers.entries()),\n      body: Buffer.from(await response.arrayBuffer())\n    };\n  }\n\n  makeHttp2Request(url, headers, body, signal) {\n    if (!http2) {\n      throw new Error(\"http2 module not available\");\n    }\n\n    const HTTP2_TIMEOUT_MS = 60000; // 60s max — prevent hung sessions\n\n    return new Promise((resolve, reject) => {\n      const urlObj = new URL(url);\n      const client = http2.connect(`https://${urlObj.host}`);\n      const chunks = [];\n      let responseHeaders = {};\n      let settled = false;\n\n      // Ensure client is always closed on settle\n      const finish = (fn) => (...args) => {\n        if (settled) return;\n        settled = true;\n        clearTimeout(hangTimeout);\n        client.close();\n        fn(...args);","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/executors/cursor.js#L309-L345","documentation":"Catch-all in the Copilot MITM handler: after mapping the model and forwarding the request via fetchRouter to the local 9Router /v1 endpoint, any thrown error (fetch failure, non-OK router response, SSE pipe error) is returned to the IDE as HTTP 500 JSON with type \"mitm_error\". The real cause is in error.message and the log line `[copilot] ...`.","triggerScenarios":"GitHub Copilot sends a chat request intercepted by the MITM proxy; resolveRouterPath/fetchRouter throws — e.g. the router is down, the route path is wrong, the request body is rejected upstream, or pipeSSE fails mid-stream.","commonSituations":"Router process not started; wrong LOCAL_PORT/proxy configuration; Copilot request uses a model that the router rejects; credentials missing on the router side; connection reset while streaming.","solutions":["Check the server log for `[copilot] <message>` to see the underlying cause.","Ensure the 9Router gateway is running and the MITM proxy points at the correct LOCAL_PORT.","Verify the model name in the Copilot request maps to a model the router can serve (model mapping config).","Test the same prompt directly against the router's /v1 endpoint (curl) to isolate MITM vs router failure.","Retry; transient fetch/SSE failures will resolve once connectivity is restored."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const routerUp = await fetch(`http://localhost:${LOCAL_PORT}/dashboard`).then(r => r.ok).catch(() => false);\nif (!routerUp) throw new Error('Start 9Router before routing Copilot traffic through the MITM proxy');","typeGuard":null,"tryCatchPattern":"try {\n  const res = await sendViaCopilotMitm(body);\n} catch (e) {\n  // mitm_error 500 — check `[copilot] <msg>` in server logs, retry once after\n  // confirming the gateway is reachable on LOCAL_PORT\n}","preventionTips":["Confirm the router /v1 endpoint answers before enabling Copilot MITM routing.","Ensure Copilot's requested model exists in the router's model mapping.","Watch `[copilot]` log lines to catch recurring forwarding failures early.","Keep LOCAL_PORT/proxy env consistent between the proxy and the gateway."],"tags":["mitm","proxy","copilot","router-forwarding"],"backgroundTag":"mitm-proxy-intercept-failure","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}