decolua/9router · error · Error

Failed to connect to server

Error message

Failed to connect to server

What it means

Thrown by GitHubService.connect() after successful GitHub device-flow authentication, when the POST of the obtained credentials to `${server}/api/cli/providers/github` returns a non-OK HTTP status. The CLI first tries to read the server's JSON body for a specific `error` message; this generic fallback is used when the server response has no `error` field or is not parseable. It means authentication with GitHub itself worked, but registering the credentials with the 9Router server failed.

Source

Thrown at src/lib/oauth/services/github.js:214

      
      const response = await fetch(`${server}/api/cli/providers/github`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
          "X-User-Id": userId,
        },
        body: JSON.stringify({
          accessToken: authResult.accessToken,
          copilotToken: authResult.copilotToken,
          userInfo: authResult.userInfo,
          copilotTokenInfo: authResult.copilotTokenInfo,
        }),
      });
      
      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error || "Failed to connect to server");
      }
      
      spinner.succeed("GitHub Copilot connected successfully!");
      console.log(`\nConnected as: ${authResult.userInfo.login}`);
    } catch (error) {
      const { error: showError } = await import("../utils/ui.js");
      showError(`GitHub connection failed: ${error.message}`);
      throw error;
    }
  }
}

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Verify the server is running and reachable at the configured `server` URL (curl `${server}/api/health`).
  2. Re-authenticate against the dashboard to get a fresh Bearer token (JWT may have expired or JWT_SECRET changed).
  3. Confirm server and CLI versions match — the /api/cli/providers/github route must exist server-side.
  4. Check server logs for the actual failure (duplicate connection, quota, DB write failure).
  5. Re-run `9router connect github`; transient 5xx may succeed on retry.

Example fix

// before (server response without `error` field loses the real cause)
if (!response.ok) {
  const errorData = await response.json();
  throw new Error(errorData.error || "Failed to connect to server");
}
// after (surface status and raw body for diagnosis)
if (!response.ok) {
  const body = await response.text();
  let msg; try { msg = JSON.parse(body).error; } catch {}
  throw new Error(msg || `Failed to connect to server (HTTP ${response.status}): ${body.slice(0, 200)}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling connect(), verify server reachability and credentials
const { server, token, userId } = await getServerCredentials();
const health = await fetch(`${server}/api/health`, {
  headers: { Authorization: `Bearer ${token}` },
}).catch(() => null);
if (!health || !health.ok) throw new Error(`Server ${server} unreachable or auth rejected`);

Type guard

function hasServerError(data) {
  return data !== null && typeof data === "object" && typeof data.error === "string";
}

Try / catch

try {
  await githubService.connect();
} catch (err) {
  if (err.message === "Failed to connect to server" || /GitHub connection failed/.test(err.message)) {
    console.error("Server rejected credential upload — check server URL, JWT freshness, and server logs:", err.message);
  } else { throw err; }
}

Prevention

When it happens

Trigger: Calling connect() when the server returns 4xx/5xx for /api/cli/providers/github — e.g. expired/invalid Bearer token in getServerCredentials(), missing or wrong X-User-Id, server route not present (version mismatch between CLI and server), or a 500 from the server without an `error` field in the JSON body.

Common situations: Stale JWT after the server was restarted with a different JWT_SECRET; server not running or pointing at the wrong URL in ~/.9router config; CLI version older than the dashboard route; server crashed mid-request returning an HTML error page that fails JSON parsing.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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