infiniflow/ragflow · error · ValueError
Failed to exchange authorization code for token: {e}
Error message
Failed to exchange authorization code for token: {e} What it means
OAuthClient.exchange_code_for_token POSTs grant_type=authorization_code with client_id, client_secret, code, redirect_uri to token_url and wraps any failure in ValueError('Failed to exchange authorization code for token: {e}') (api/apps/auth/oauth.py:79). The original exception text is preserved, so the message usually embeds the provider's HTTP status or transport error.
Source
Thrown at api/apps/auth/oauth.py:79
return authorization_url
def exchange_code_for_token(self, code):
"""
Exchange authorization code for access token.
"""
try:
payload = {"client_id": self.client_id, "client_secret": self.client_secret, "code": code, "redirect_uri": self.redirect_uri, "grant_type": "authorization_code"}
response = sync_request(
"POST",
self.token_url,
data=payload,
headers={"Accept": "application/json"},
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}")
async def async_exchange_code_for_token(self, code):
"""
Async variant of exchange_code_for_token using httpx.
"""
payload = {
"client_id": self.client_id,
"client_secret": self.client_secret,
"code": code,
"redirect_uri": self.redirect_uri,
"grant_type": "authorization_code",
}
try:
response = await async_request(
"POST",
self.token_url,
data=payload,
headers={"Accept": "application/json"},View on GitHub (pinned to 554fb1133a)
Solutions
- Compare the redirect_uri sent in the token request with the one in the initial authorization request and the provider's registered callback - they must match exactly.
- Verify client_id/client_secret are current and copied without whitespace.
- Ensure the code is used exactly once and immediately; inspect for duplicate callback invocations (logs show two exchanges with the same code).
- Curl the token endpoint manually with the same payload to see the provider's raw error (invalid_grant, invalid_client, etc.).
Example fix
# reproduce to see the provider's real error curl -X POST https://provider.example/oauth/token \ -d grant_type=authorization_code -d client_id=ID -d client_secret=SECRET \ -d code=THE_CODE -d redirect_uri='https://your-app/v1/auth/oauth/callback'
Defensive patterns
Strategy: try-catch
Validate before calling
def exchange_precheck(provider_cfg, redirect_uri):
assert provider_cfg["client_id"] and provider_cfg["client_secret"]
assert provider_cfg["token_url"].startswith("https://")
assert redirect_uri == provider_cfg["registered_redirect_uri"] # byte-exact Try / catch
try:
token = client.exchange_code_for_token(code)
except ValueError as e:
msg = str(e)
if "invalid_grant" in msg:
restart_authorization() # code expired/used - get a fresh one
elif "invalid_client" in msg:
raise ConfigError("OAuth client secret wrong - update provider config")
else:
raise Prevention
- Register one canonical redirect_uri and build both authorization and token requests from the same constant.
- Never reuse an authorization code; guard against duplicate callback deliveries.
- Rotate secrets on schedule and update RAGFlow config atomically.
- Test the token endpoint with curl when adding a new provider.
When it happens
Trigger: redirect_uri differs byte-for-byte from the one registered with the provider (trailing slash, http vs https, port); client_secret wrong or rotated; the authorization code was already used once or expired (typically ~1-10 min); code from a different client_id; network/TLS failure reaching token_url; provider returns non-JSON on error making response.json() raise.
Common situations: Local development behind a proxy where the externally visible URL differs from redirect_uri; secrets rotated without updating RAGFlow config; double callback firing (browser retry, duplicate route) consuming the one-time code; clock/token endpoint typos in hand-written OAuth config.
Related errors
- Failed to fetch user info: {e}
- Unsupported type: {channel_type}
- Failed to fetch github user info: {e}
- 100
- Invalid channel name: {channel}
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/55e2712bbf2405be.
Report an issue: GitHub.