prestodb/presto · error · ParseException
/userinfo response missing principal field %s
Error message
/userinfo response missing principal field %s
What it means
NimbusOAuth2Client's custom /userinfo response parser requires the configured principal field (oauth2.principal-field, default 'sub') to be present and non-null in the JSON body; otherwise it throws a ParseException.
Source
Thrown at presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java:598
}
// 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();
String principal = (String) body.get(principalField);
if (principal == null) {
throw new ParseException(String.format("/userinfo response missing principal field %s", principalField));
}
if (!principalField.equals("sub") && body.get("sub") == null) {
body.put("sub", principal);
httpResponse.setBody(body.toJSONString());
}
Object audClaim = body.get("aud");
// only validate aud claim if it exists
if (audClaim != null) {
List<String> audiences;
if (audClaim instanceof String) {
audiences = List.of((String) audClaim);
}
else if (audClaim instanceof List<?>) {
audiences = ((List<?>) audClaim).stream()
.filter(String.class::isInstance)View on GitHub (pinned to 55bb57d202)
Solutions
- Set oauth2.principal-field to a claim actually present in the /userinfo response (often 'sub')
- Request the required scopes (email, profile) in oauth2.scopes so optional fields are populated
- Log/inspect the raw /userinfo JSON from the IdP to see available claims
- If using a custom claim, confirm the IdP maps it into the userinfo response
Example fix
// before <property name="oauth2.principal-field">email</property> // email scope not granted // after <property name="oauth2.principal-field">sub</property>
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check config: the principal-field must be a claim the IdP userinfo response actually returns
String body = fetchUserInfoJson();
if (!body.contains("\"" + principalField + "\"")) { throw new IllegalStateException("principal-field '" + principalField + "' absent from /userinfo response"); } Type guard
boolean hasPrincipalField(org.json.JSONObject body, String principalField) { return body != null && body.opt(principalField) instanceof String && !((String) body.opt(principalField)).isEmpty(); } Try / catch
try { return parseUserInfoResponse(httpResponse); } catch (com.nimbusds.oauth2.sdk.ParseException e) { LOG.error("/userinfo missing principal field; check oauth2.principal-field and scopes"); throw e; } Prevention
- Set oauth2.principal-field to 'sub' unless you are certain the IdP returns the custom claim
- Request the scopes (email/profile) that populate the configured principal field
- Dump the raw /userinfo JSON once during setup to confirm claim names
- Re-validate config after IdP upgrades that change claim sets
When it happens
Trigger: The IdP's /userinfo response lacks the field named by principalField — e.g. principal-field configured as 'email' but the token lacks the email scope so the response omits it, or a custom field name that the IdP never returns.
Common situations: Missing OIDC scopes (email/profile) at the IdP so optional claim fields are omitted, typo in principal-field config, IdP returning different claim names than configured, custom IdPs that only return 'sub'.
Related errors
- Unsupported 'aud' claim type in /userinfo response
- iceberg.rest.auth.oauth2 requires either a credential or a t
- UserInfo endpoint returned error:
- ACCUMULO_TABLE_EXISTS
- UNEXPECTED_ACCUMULO_ERROR
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/2d7417b6bc949419.
Report an issue: GitHub.