prestodb/presto · error · RuntimeException

UserInfo endpoint returned error:

Error message

UserInfo endpoint returned error: 

What it means

NimbusOAuth2Client.fetchUserInfoClaims got an error response from the OAuth2 UserInfo endpoint (non-success HTTP status); the endpoint's error body is appended. The claims fetch needed to establish the authenticated principal failed.

Source

Thrown at presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java:576

    /**
     * Fetches user information claims from the UserInfo endpoint.
     *
     * @param accessToken the OAuth2 access token for authentication
     * @return JWTClaimsSet containing user information
     * @throws ParseException if the response cannot be parsed
     * @throws RuntimeException if the HTTP request fails
     */
    private JWTClaimsSet fetchUserInfoClaims(String accessToken) throws ParseException
    {
        UserInfoResponse response = httpClient.execute(
                new UserInfoRequest(userinfoUrl.get(), new BearerAccessToken(accessToken)),
                this::parse);

        if (!response.indicatesSuccess()) {
            UserInfoErrorResponse errorResponse = response.toErrorResponse();
            LOG.error("Received error from UserInfo endpoint: %s", errorResponse.getErrorObject());
            throw new RuntimeException("UserInfo endpoint returned error: " + errorResponse.getErrorObject());
        }

        return response.toSuccessResponse().getUserInfo().toJWTClaimsSet();
    }

    // Using this parsing method for our /userinfo response from the IdP in order to allow for different principal
    // fields as defined, and in the absence of the `sub` claim. This is a "hack" solution to alter the claims
    // present in the response before calling the parser provided by the oidc sdk, which fails hard if the
    // `sub` claim is missing.
    public UserInfoResponse parse(HTTPResponse httpResponse)
            throws ParseException
    {
        // Check status code first and only process payload if successful
        if (httpResponse.getStatusCode() != 200) {
            return UserInfoErrorResponse.parse(httpResponse);
        }

        JSONObject body = httpResponse.getBodyAsJSONObject();

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Read the logged 'Received error from UserInfo endpoint' error object (e.g. invalid_token, insufficient_scope) to identify the cause
  2. Verify oauth2.access-token.audiences includes the audience the IdP puts on access tokens
  3. Re-authenticate to obtain a fresh access token if expired or revoked
  4. Confirm the proxy/load balancer forwards the Authorization: Bearer header to /userinfo

Example fix

// before
// oauth2.access-token.audiences not set; IdP requires audience=coordinator
// after
<property name="oauth2.access-token.audiences">coordinator</property>
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: curl -H "Authorization: Bearer <token>" https://idp/userinfo to confirm the token is accepted before wiring the flow

Type guard

boolean userInfoResponseOk(int status) { return status >= 200 && status < 300; }

Try / catch

try { return client.getOAuth2Response(code, callbackUri, nonce); } catch (RuntimeException e) { if (e.getMessage().startsWith("UserInfo endpoint returned error")) { LOG.warn("UserInfo rejected token: {}", e.getMessage()); reauthenticate(); } throw e; }

Prevention

When it happens

Trigger: GET /userinfo with a bearer token the IdP rejects: access token expired or revoked, wrong audience/scope, token issued for a different IdP instance, or the userinfo endpoint is down/misconfigured.

Common situations: Clock skew after authentication making the token appear expired, oauth2.access-token audiences config missing the required audience, IdP changed userinfo requirements, network/proxy stripping the Authorization header.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/ca1de12d3554da2a. Report an issue: GitHub.