decolua/9router · error

Failed to get user info

Error message

Failed to get user info

What it means

Thrown by IFlowService.getUserInfo() when the user-info endpoint returns HTTP 200 but the JSON envelope has `success: false`. iFlow wraps payloads in { success, data }; a 200 with success:false means the request was transport-fine but the service refused to return profile data (e.g. the accessToken query parameter was rejected). Note this generic variant carries no upstream detail.

Source

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

  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`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
        "X-User-Id": userId,
      },

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Log/inspect the full response body to see iFlow's actual reason field alongside success:false.
  2. Confirm you pass tokens.access_token (not another field) into getUserInfo.
  3. Re-run connect() to get a fresh token; the current one may be effectively revoked.
  4. Check whether iFlow changed its response envelope and update the check accordingly.

Example fix

// before
if (!result.success) {
  throw new Error("Failed to get user info");
}
// after
if (!result.success) {
  throw new Error(`Failed to get user info: ${result.message || result.error || JSON.stringify(result).slice(0, 200)}`);
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the token before trusting the envelope-based endpoint
function looksLikeJwt(token) {
  return typeof token === "string" && token.split(".").length >= 2;
}
if (!looksLikeJwt(tokens.access_token)) throw new Error("access_token malformed — wrong field from exchange response?");

Type guard

function isSuccessfulUserInfo(result) {
  return result !== null && typeof result === "object" && result.success === true && result.data != null;
}

Try / catch

try {
  const userInfo = await iflowService.getUserInfo(tokens.access_token);
} catch (err) {
  if (err.message === "Failed to get user info") {
    console.error("iFlow returned success:false with no detail — inspect the raw response and token field used.");
  } else { throw err; }
}

Prevention

When it happens

Trigger: getUserInfo(accessToken) receives an HTTP 200 whose body parses to { success: false } — typically an access token that is syntactically accepted but not authorized for the profile endpoint, an empty/garbage token string, or an API change where the envelope field was renamed.

Common situations: Passing the wrong token field from the exchange response (e.g. id_token instead of access_token); iFlow deprecating the `success` envelope without the CLI being updated; token from a different iFlow environment (staging vs prod).

Related errors


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