infiniflow/ragflow · error · ValueError

Failed to fetch user info: {e}

Error message

Failed to fetch user info: {e}

What it means

Generic OAuthClient.fetch_user_info GETs userinfo_url with a Bearer access token, then calls normalize_user_info; any HTTP, transport, JSON, or normalization failure is re-raised as ValueError('Failed to fetch user info: {e}') (api/apps/auth/oauth.py:116). Used by the plain 'oauth2' client type, so endpoint and response-shape problems dominate.

Source

Thrown at api/apps/auth/oauth.py:116

                timeout=self.http_request_timeout,
            )
            response.raise_for_status()
            return response.json()
        except Exception as e:
            raise ValueError(f"Failed to exchange authorization code for token: {e}")

    def fetch_user_info(self, access_token, **kwargs):
        """
        Fetch user information using access token.
        """
        try:
            headers = {"Authorization": f"Bearer {access_token}"}
            response = sync_request("GET", self.userinfo_url, headers=headers, timeout=self.http_request_timeout)
            response.raise_for_status()
            user_info = response.json()
            return self.normalize_user_info(user_info)
        except Exception as e:
            raise ValueError(f"Failed to fetch user info: {e}")

    async def async_fetch_user_info(self, access_token, **kwargs):
        """Async variant of fetch_user_info using httpx."""
        headers = {"Authorization": f"Bearer {access_token}"}
        try:
            response = await async_request(
                "GET",
                self.userinfo_url,
                headers=headers,
                timeout=self.http_request_timeout,
            )
            response.raise_for_status()
            user_info = response.json()
            return self.normalize_user_info(user_info)
        except Exception as e:
            raise ValueError(f"Failed to fetch user info: {e}")

    def normalize_user_info(self, user_info):

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Curl userinfo_url with the Bearer token and confirm it returns JSON with an email and a username-compatible field.
  2. Correct userinfo_url in the provider configuration (for OIDC providers prefer type 'oidc' + issuer so discovery fills it in).
  3. Verify the token exchange succeeded immediately before this call - a failed exchange often surfaces here.
  4. Confirm the token's scopes include profile/email access.

Example fix

# verify endpoint + token
curl -H 'Authorization: Bearer $ACCESS_TOKEN' https://provider.example/userinfo
# expect JSON with email/username fields
Defensive patterns

Strategy: try-catch

Validate before calling

def userinfo_precheck(cfg, token):
    assert cfg.get("userinfo_url"), "userinfo_url missing in oauth2 config"
    import requests
    r = requests.get(cfg["userinfo_url"], headers={"Authorization": f"Bearer {token}"}, timeout=10)
    assert r.ok and isinstance(r.json(), dict), f"userinfo probe failed: {r.status_code}"

Try / catch

try:
    info = client.fetch_user_info(access_token)
except ValueError as e:
    if "401" in str(e):
        # token unusable - do not retry with the same token
        raise SessionExpired("re-authentication required")
    raise

Prevention

When it happens

Trigger: Access token invalid or expired (401); userinfo_url wrong or missing in the provider config; provider's userinfo response lacks the fields normalize_user_info expects; response is not JSON; network failure or timeout hitting userinfo_url.

Common situations: Hand-configured generic OAuth2 providers where the userinfo endpoint was guessed; field-name mismatches between providers (sub vs username, no email claim); tokens expiring between exchange and userinfo call.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/532fcc14481f5efc. Report an issue: GitHub.