decolua/9router · error · Error

Failed to get user info: ${error}

Error message

Failed to get user info: ${error}

What it means

Thrown by IFlowService.getUserInfo() when the iFlow user-info endpoint returns a non-OK HTTP status. The raw response text is embedded in the message. This happens after a successful token exchange, so the access token exists but the user-info call rejected it or the endpoint itself failed.

Source

Thrown at src/lib/oauth/services/iflow.js:80

    return await response.json();
  }

  /**
   * Get user info from iFlow
   */
  async getUserInfo(accessToken) {
    const response = await fetch(
      `${this.config.userInfoUrl}?accessToken=${encodeURIComponent(accessToken)}`,
      {
        headers: {
          Accept: "application/json",
        },
      }
    );

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Failed to get user info: ${error}`);
    }

    const result = await response.json();

    if (!result.success) {
      throw new Error("Failed to get user info");
    }

    return result.data;
  }

  /**
   * Save iFlow tokens to server
   */
  async saveTokens(tokens, userInfo) {
    const { server, token, userId } = getServerCredentials();

    const response = await fetch(`${server}/api/cli/providers/iflow`, {

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Inspect the embedded body in the message for the actual status/cause (401/403 → token problem, 404 → endpoint changed).
  2. Verify the access token from exchangeCode is the one iFlow expects (access_token vs apikey field mix-up).
  3. Check IFLOW_CONFIG.userInfoUrl against current iFlow API docs — the endpoint may have moved.
  4. Retry after a short wait if the message indicates 429/5xx.
  5. Restart the full connect() flow to obtain a fresh token.

Example fix

// before
if (!response.ok) {
  const error = await response.text();
  throw new Error(`Failed to get user info: ${error}`);
}
// after
if (!response.ok) {
  const error = await response.text();
  throw new Error(`Failed to get user info (HTTP ${response.status}): ${error}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check the token looks usable before calling user info
if (!accessToken || typeof accessToken !== "string" || accessToken.length < 10) {
  throw new Error("Refusing to call getUserInfo with a missing/short access token");
}

Try / catch

try {
  const userInfo = await iflowService.getUserInfo(tokens.access_token);
} catch (err) {
  if (/Failed to get user info/.test(err.message)) {
    if (/401|403|Unauthorized|Forbidden/i.test(err.message)) {
      console.error("Token rejected for user info — obtain a fresh token via connect().");
    } else if (/404|Not Found/i.test(err.message)) {
      console.error("userInfoUrl changed — update IFLOW_CONFIG.userInfoUrl.");
    }
  } else { throw err; }
}

Prevention

When it happens

Trigger: getUserInfo(accessToken) is called and the server answers 401/403 (token invalid, expired, or lacking scope for user info), 404 (userInfoUrl changed), or 5xx (upstream outage); also when a proxy intercepts and returns an HTML error page.

Common situations: iFlow access token already invalid despite the exchange succeeding (clock skew, token type mismatch); userInfoUrl in IFLOW_CONFIG outdated after an API change; corporate proxy blocking the endpoint; rate limiting returning 429.

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/1796121ca973b3fd. Report an issue: GitHub.