infiniflow/ragflow · error · ValueError
Failed to fetch github user info: {e}
Error message
Failed to fetch github user info: {e} What it means
GithubOAuthClient.fetch_user_info calls GET /user and GET /user/emails with the access token and wraps any failure in ValueError('Failed to fetch github user info: {e}') (api/apps/auth/github.py:52). The except is broad: it fires on HTTP errors (401 bad token, 403 scope missing), network/timeouts, JSON decode errors, and a subtle TypeError when the user has no 'primary' email - next(...) returns None and ['email'] raises inside the try.
Source
Thrown at api/apps/auth/github.py:52
super().__init__(config)
def fetch_user_info(self, access_token, **kwargs):
"""
Fetch GitHub user info (synchronous).
"""
user_info = {}
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.update(response.json())
email_response = sync_request("GET", self.userinfo_url + "/emails", headers=headers, timeout=self.http_request_timeout)
email_response.raise_for_status()
email_info = email_response.json()
user_info["email"] = next((email for email in email_info if email["primary"]), None)["email"]
return self.normalize_user_info(user_info)
except Exception as e:
raise ValueError(f"Failed to fetch github user info: {e}")
async def async_fetch_user_info(self, access_token, **kwargs):
"""Async variant of fetch_user_info using httpx."""
user_info = {}
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.update(response.json())
email_response = await async_request(
"GET",
self.userinfo_url + "/emails",View on GitHub (pinned to 554fb1133a)
Solutions
- Ensure the GitHub OAuth app requests the 'user:email' scope and re-authorize so the token carries it.
- Test the token directly: curl -H 'Authorization: Bearer <token>' https://api.github.com/user/emails must return a list containing an entry with "primary": true.
- For GitHub Enterprise, verify github_api_base/host config so userinfo_url resolves to <host>/api/v3/user.
- Handle users with no primary email (corporate SAML-only accounts) by falling back to login-based identity or requiring an email.
Example fix
# verify token + scope
curl -H 'Authorization: Bearer $TOKEN' https://api.github.com/user/emails
# expect: [{"email": "...", "primary": true, ...}] Defensive patterns
Strategy: try-catch
Validate before calling
import requests
def github_token_ok(token, base="https://api.github.com"):
h = {"Authorization": f"Bearer {token}"}
u = requests.get(f"{base}/user", headers=h, timeout=10)
e = requests.get(f"{base}/user/emails", headers=h, timeout=10)
emails = e.json() if e.ok else []
return u.ok and e.ok and any(x.get("primary") for x in emails) Try / catch
try:
info = client.fetch_user_info(access_token)
except ValueError as e:
msg = str(e)
if "401" in msg:
restart_oauth_flow() # token expired - re-authenticate
elif "NoneType" in msg or "primary" in msg:
raise UserVisibleError("Your GitHub account has no primary email; add one and retry")
else:
retry_or_alert(msg) Prevention
- Request the user:email scope in the GitHub OAuth app from day one.
- Handle users without a primary email gracefully (fall back to login).
- For GitHub Enterprise, smoke-test /api/v3/user reachability from the server.
- Keep access tokens short-lived and re-exchange rather than reusing stale ones.
When it happens
Trigger: Access token expired or revoked (401 on /user); token lacks the 'user:email' scope so /user/emails returns [] or 403; GitHub Enterprise userinfo_url misconfigured (wrong github_api_base); user has no primary/verified email on file; transient network failure or timeout.
Common situations: OAuth app created without requesting user:email scope; self-hosted GitHub Enterprise with an API base URL missing /api/v3; users with private emails and empty email list; long-running sessions with expired tokens.
Related errors
- Failed to exchange authorization code for token: {e}
- Failed to fetch user info: {e}
- Login failed: invalid JSON response ({exc})
- HTTP {response.status_code}: {response.text}
- GitHub search returned no items.
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/821d796ccb729138.
Report an issue: GitHub.