decolua/9router · error

http2 module not available

Error message

http2 module not available

What it means

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] ...`.

Source

Thrown at open-sse/executors/cursor.js:327

  async makeFetchRequest(url, headers, body, signal, proxyOptions = null) {
    const response = await proxyAwareFetch(url, {
      method: "POST",
      headers,
      body,
      signal
    }, proxyOptions);

    return {
      status: response.status,
      headers: Object.fromEntries(response.headers.entries()),
      body: Buffer.from(await response.arrayBuffer())
    };
  }

  makeHttp2Request(url, headers, body, signal) {
    if (!http2) {
      throw new Error("http2 module not available");
    }

    const HTTP2_TIMEOUT_MS = 60000; // 60s max — prevent hung sessions

    return new Promise((resolve, reject) => {
      const urlObj = new URL(url);
      const client = http2.connect(`https://${urlObj.host}`);
      const chunks = [];
      let responseHeaders = {};
      let settled = false;

      // Ensure client is always closed on settle
      const finish = (fn) => (...args) => {
        if (settled) return;
        settled = true;
        clearTimeout(hangTimeout);
        client.close();
        fn(...args);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Check the server log for `[copilot] <message>` to see the underlying cause.
  2. Ensure the 9Router gateway is running and the MITM proxy points at the correct LOCAL_PORT.
  3. Verify the model name in the Copilot request maps to a model the router can serve (model mapping config).
  4. Test the same prompt directly against the router's /v1 endpoint (curl) to isolate MITM vs router failure.
  5. Retry; transient fetch/SSE failures will resolve once connectivity is restored.
Defensive patterns

Strategy: retry

Validate before calling

const routerUp = await fetch(`http://localhost:${LOCAL_PORT}/dashboard`).then(r => r.ok).catch(() => false);
if (!routerUp) throw new Error('Start 9Router before routing Copilot traffic through the MITM proxy');

Try / catch

try {
  const res = await sendViaCopilotMitm(body);
} catch (e) {
  // mitm_error 500 — check `[copilot] <msg>` in server logs, retry once after
  // confirming the gateway is reachable on LOCAL_PORT
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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