vxcontrol/pentagi · error
failed to parse Google ID Token claims: %w
Error message
failed to parse Google ID Token claims: %w
What it means
Thrown in newGoogleEmailResolver when idToken.Claims(&claims) fails to unmarshal the verified Google ID Token payload into googleTokenClaims (nonce, email, email_verified). With go-oidc v3 this almost always means the ID token payload is not valid JSON or the json.Unmarshal into the claims struct failed, which is practically impossible for a genuinely verified Google token — so it usually indicates a corrupted, truncated, or substituted id_token string, or a custom/misrouted OIDC provider whose claims shape differs.
Source
Thrown at backend/pkg/server/oauth/google.go:46
}
verifier := provider.Verifier(&oidc.Config{ClientID: clientID})
idToken, err := verifier.Verify(ctx, oidToken)
if err != nil {
return "", false, fmt.Errorf("could not verify Google ID Token: %w", err)
}
if idToken.Nonce != nonce {
return "", false, fmt.Errorf("nonce mismatch in Google ID Token")
}
if err = idToken.VerifyAccessToken(token.AccessToken); err != nil {
return "", false, fmt.Errorf("failed to verify Google Access Token: %w", err)
}
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w", err)
}
if claims.Nonce != nonce {
return "", false, fmt.Errorf("nonce mismatch in Google ID Token claims")
}
if claims.Email == "" {
return "", false, fmt.Errorf("email is empty in Google ID Token claims")
}
return claims.Email, claims.EmailVerified, nil
}
}
func NewGoogleOAuthClient(clientID, clientSecret, redirectURL string) OAuthClient {
return NewOAuthClient("google", &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,View on GitHub (pinned to ea665308ba)
Solutions
- Log the raw id_token (first ~50 chars) and run it through jwt.io / a JWKS decode to inspect the payload for corruption.
- Verify no proxy or middleware rewrites token response bodies (check HTTP_PROXY, ingress, service mesh MITM settings).
- Ensure the clientID passed to oidc.Config matches the OAuth client that issued the token.
- Update github.com/coreos/go-oidc/v3 to the latest version; very old versions had stricter claim decoding behavior.
- If the issuer is intentionally not Google, use the matching provider discovery URL instead of hard-coded accounts.google.com.
Example fix
// before
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w", err)
}
// after
claims := googleTokenClaims{}
if err := idToken.Claims(&claims); err != nil {
return "", false, fmt.Errorf("failed to parse Google ID Token claims: %w (raw token prefix: %.40s)", err, oidToken)
} Defensive patterns
Strategy: try-catch
Validate before calling
const raw = token.Extra("id_token")
if s, ok := raw.(string); !ok || s == "" {
return fmt.Errorf("id_token missing or not a string")
}
parts := strings.Split(s, ".")
if len(parts) != 3 {
return fmt.Errorf("id_token is not a JWT")
}
if payload, err := base64.RawURLEncoding.DecodeString(parts[1]); err != nil || !json.Valid(payload) {
return fmt.Errorf("id_token payload is not valid JSON")
} Type guard
func hasValidIDToken(t *oauth2.Token) bool {
s, ok := t.Extra("id_token").(string)
return ok && strings.Count(s, ".") == 2
} Try / catch
email, verified, err := resolver.Resolve(ctx, nonce, token)
if err != nil {
if strings.Contains(err.Error(), "failed to parse Google ID Token claims") {
logger.WithError(err).Error("corrupt or non-standard Google ID token payload; inspect raw token and proxy config")
return fmt.Errorf("oauth login failed: malformed ID token")
}
return err
} Prevention
- Always verify the JWT structure (3 dot-separated segments) before exchange debugging
- Check for proxies/middleware that rewrite token response bodies
- Keep github.com/coreos/go-oidc/v3 up to date
- Log a truncated token prefix on failure for diagnosis
When it happens
Trigger: Google OAuth callback processed via AuthAuthorize: the id_token passed verifier.Verify, VerifyAccessToken succeeded, but Claims() cannot decode the token payload into the googleTokenClaims struct (malformed JSON payload, wrong token supplied as id_token, or a proxy/MTM returning mangled token bodies).
Common situations: An HTTP proxy or service mesh (MITM) altering response bodies; a misconfigured OIDC base URL pointing at a non-Google issuer that issues tokens without standard claim JSON; token string truncated by logging/rewriting middleware; storing/replaying the token through something that corrupts it (e.g. HTML-escaped state).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- id_token is not present in the token
- could not verify Google ID Token: %w
- failed to verify Google Access Token: %w
- could not create Google OpenID client: %w
- nonce mismatch in Google ID Token
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/2da6cbe826885481.
Report an issue: GitHub.