prestodb/presto · error · ChallengeFailedException
Error while fetching access token:
Error message
Error while fetching access token:
What it means
Thrown by NimbusOAuth2Client when the HTTP exchange to the OAuth2 token endpoint fails while exchanging an authorization code or refresh token; the exception detail is appended. It means the server's token could not be obtained, so the login challenge fails rather than the request being malformed.
Source
Thrown at presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java:454
private <T extends AccessTokenResponse> T getTokenResponse(String code, URI callbackUri, NimbusAirliftHttpClient.Parser<T> parser)
throws ChallengeFailedException
{
return getTokenResponse(new AuthorizationCodeGrant(new AuthorizationCode(code), callbackUri), parser);
}
private <T extends AccessTokenResponse> T getTokenResponse(String refreshToken, NimbusAirliftHttpClient.Parser<T> parser)
throws ChallengeFailedException
{
return getTokenResponse(new RefreshTokenGrant(new RefreshToken(refreshToken)), parser);
}
private <T extends AccessTokenResponse> T getTokenResponse(AuthorizationGrant authorizationGrant, NimbusAirliftHttpClient.Parser<T> parser)
throws ChallengeFailedException
{
T tokenResponse = httpClient.execute(new TokenRequest(tokenUrl, clientAuth, authorizationGrant, scope), parser);
if (!tokenResponse.indicatesSuccess()) {
throw new ChallengeFailedException("Error while fetching access token: " + tokenResponse.toErrorResponse().toJSONObject());
}
return tokenResponse;
}
/**
* Retrieves JWT claims for the given access token.
*
* IMPORTANT: This method should NOT be used for extracting the principal field.
* Per OIDC specification, the principal should come from the ID token, not the access token.
* This method is kept for backward compatibility and the getClaims() API method.
*
* @param accessToken the access token value
* @return Optional containing claims from access token or UserInfo endpoint
*/
private Optional<JWTClaimsSet> getJWTClaimsSet(String accessToken)
{
// Try parsing access token as JWT
Optional<JWTClaimsSet> claims = parseAccessToken(accessToken);View on GitHub (pinned to 55bb57d202)
Solutions
- Start a fresh login flow instead of replaying the old callback URL (codes are single-use)
- Verify oauth2.client-id, client-secret and redirect-uri match the IdP application registration exactly
- Check the appended IdP error JSON in the message (e.g. invalid_grant) to identify the exact rejection
- Ensure the callback is not retried/double-delivered by proxies or browser refresh
Example fix
// before // browser refresh of POST /oauth2/callback?code=OLD_CODE -> invalid_grant // after // redirect user to a fresh /oauth2/authentication/challenge flow
Defensive patterns
Strategy: retry
Validate before calling
// Verify oauth2.client-id, client-secret, redirect-uri are non-empty and match the IdP app registration before starting the flow
Type guard
boolean tokenRequestConfigValid(String clientId, String secret, java.net.URI redirectUri) { return clientId != null && !clientId.isEmpty() && secret != null && !secret.isEmpty() && redirectUri != null; } Try / catch
try { return client.getOAuth2Response(code, callbackUri, nonce); } catch (ChallengeFailedException e) { if (!e.getMessage().contains("invalid_grant")) retryWithBackoff(); else restartLoginFlow(); throw e; } Prevention
- Never replay authorization codes; always start a fresh challenge after any token-endpoint error
- Match redirect-uri byte-for-byte with the IdP registration
- Alert on the embedded IdP error JSON in the message to classify invalid_grant vs unauthorized_client
- Prevent proxies from double-delivering the OAuth2 callback
When it happens
Trigger: Exchanging the authorization code when the code is expired/already used, redirect_uri or client credentials mismatch the original request, or the IdP rejects the grant (invalid_grant, unauthorized_client).
Common situations: Replaying an old callback URL after login already completed, mismatched oauth2.client-id/secret or redirect-uri config, clock skew, callback retried by the browser, load balancer double-delivering the callback.
Related errors
- UserInfo endpoint returned error:
- iceberg.rest.auth.oauth2 requires either a credential or a t
- Missing nonce
- Cannot validate tokens
- /userinfo response missing principal field %s
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/d99d9eff5e0b1916.
Report an issue: GitHub.