dgraph-io/dgraph · error

unable to parse jwt token:%v

Error message

unable to parse jwt token:%v

What it means

Generic failure wrapper: any error produced while parsing/validating the JWT (signature invalid, token expired, malformed token, keyfunc errors like kid/alg failures) is re-wrapped as "unable to parse jwt token:%v". The underlying cause in %v is what actually matters for debugging. Note the JWK-URL loop swallows per-URL errors and only the last one is wrapped here.

Source

Thrown at graphql/authorization/auth.go:412

				algo, _ := token.Header["alg"].(string)
				if algo != a.Algo {
					return nil, errors.Errorf("unexpected signing method: Expected %s Found %s",
						a.Algo, algo)
				}

				switch a.SigningMethod.(type) {
				case *jwt.SigningMethodHMAC:
					return []byte(a.VerificationKey), nil
				case *jwt.SigningMethodRSA:
					return a.RSAPublicKey, nil
				}

				return nil, errors.Errorf("couldn't parse signing method from token header: %s", algo)
			})
	}

	if err != nil {
		return nil, errors.Errorf("unable to parse jwt token:%v", err)
	}

	claims, ok := token.Claims.(*CustomClaims)
	if !ok || !token.Valid {
		return nil, errors.Errorf("claims in jwt token is not map claims")
	}

	if err := claims.validateAudience(); err != nil {
		return nil, err
	}
	return claims, nil
}

// FetchJWKs fetches the JSON Web Key sets for the JWKUrls. It returns an error if
// the fetching of key is failed even for one of the JWKUrl.
func (a *AuthMeta) FetchJWKs() error {
	if len(a.JWKUrls) == 0 {
		return errors.Errorf("No JWKUrl supplied")

View on GitHub (pinned to 759e242be6)

Solutions

  1. Read the wrapped cause after "unable to parse jwt token:" to identify the real problem.
  2. Decode the token locally (jwt.io or jwt.Parse) to check expiry, signature, and header.
  3. Sync clocks (NTP) if the cause is token expired with skew.
  4. Confirm the verification key/JWK set matches the issuer's current signing key.
  5. Return structured errors to clients (401 with reason) instead of the raw wrapped message.

Example fix

// before
return nil, errors.Errorf("unable to parse jwt token:%v", err)
// after
if errors.Is(err, jwt.ErrTokenExpired) {
    return nil, status.Error(codes.Unauthenticated, "token expired")
}
return nil, errors.Wrap(err, "unable to parse jwt token")
Defensive patterns

Strategy: try-catch

Validate before calling

func precheckToken(jwtStr string) error {
    parts := strings.Split(jwtStr, ".")
    if len(parts) != 3 { return errors.New("malformed JWT: expected 3 segments") }
    return nil
}

Try / catch

claims, err := auth.ExtractCustomClaims(ctx, jwtStr)
if err != nil {
    var cause string
    if pkgErr, ok := err.(interface{ Cause() error }); ok { cause = pkgErr.Cause().Error() }
    log.Printf("JWT rejected: %v (cause: %s)", err, cause)
    return nil, status.Error(codes.Unauthenticated, "invalid or expired token")
}

Prevention

When it happens

Trigger: After validateThroughJWKUrl or the static-key ParseWithClaims returns err != nil, validateJWTCustomClaims wraps it. Any prior error in this list (351–355) or jwt library errors (signature invalid, token expired, malformed) surface through here when ExtractCustomClaims is called.

Common situations: Client sends expired/tampered tokens; clock skew between services; token truncated by proxy or header size limit; all configured JWK URLs failed validation; key mismatch after rotation.

Understand the failure class

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/366a990ded7a0f36. Report an issue: GitHub.