{"record":{"id":"366a990ded7a0f36","repo":"dgraph-io/dgraph","slug":"unable-to-parse-jwt-token-v","errorCode":null,"errorMessage":"unable to parse jwt token:%v","messagePattern":"unable to parse jwt token:(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":412,"sourceCode":"\t\t\t\talgo, _ := token.Header[\"alg\"].(string)\n\t\t\t\tif algo != a.Algo {\n\t\t\t\t\treturn nil, errors.Errorf(\"unexpected signing method: Expected %s Found %s\",\n\t\t\t\t\t\ta.Algo, algo)\n\t\t\t\t}\n\n\t\t\t\tswitch a.SigningMethod.(type) {\n\t\t\t\tcase *jwt.SigningMethodHMAC:\n\t\t\t\t\treturn []byte(a.VerificationKey), nil\n\t\t\t\tcase *jwt.SigningMethodRSA:\n\t\t\t\t\treturn a.RSAPublicKey, nil\n\t\t\t\t}\n\n\t\t\t\treturn nil, errors.Errorf(\"couldn't parse signing method from token header: %s\", algo)\n\t\t\t})\n\t}\n\n\tif err != nil {\n\t\treturn nil, errors.Errorf(\"unable to parse jwt token:%v\", err)\n\t}\n\n\tclaims, ok := token.Claims.(*CustomClaims)\n\tif !ok || !token.Valid {\n\t\treturn nil, errors.Errorf(\"claims in jwt token is not map claims\")\n\t}\n\n\tif err := claims.validateAudience(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn claims, nil\n}\n\n// FetchJWKs fetches the JSON Web Key sets for the JWKUrls. It returns an error if\n// the fetching of key is failed even for one of the JWKUrl.\nfunc (a *AuthMeta) FetchJWKs() error {\n\tif len(a.JWKUrls) == 0 {\n\t\treturn errors.Errorf(\"No JWKUrl supplied\")","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L394-L430","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the wrapped cause after \"unable to parse jwt token:\" to identify the real problem.","Decode the token locally (jwt.io or jwt.Parse) to check expiry, signature, and header.","Sync clocks (NTP) if the cause is token expired with skew.","Confirm the verification key/JWK set matches the issuer's current signing key.","Return structured errors to clients (401 with reason) instead of the raw wrapped message."],"exampleFix":"// before\nreturn nil, errors.Errorf(\"unable to parse jwt token:%v\", err)\n// after\nif errors.Is(err, jwt.ErrTokenExpired) {\n    return nil, status.Error(codes.Unauthenticated, \"token expired\")\n}\nreturn nil, errors.Wrap(err, \"unable to parse jwt token\")","handlingStrategy":"try-catch","validationCode":"func precheckToken(jwtStr string) error {\n    parts := strings.Split(jwtStr, \".\")\n    if len(parts) != 3 { return errors.New(\"malformed JWT: expected 3 segments\") }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"claims, err := auth.ExtractCustomClaims(ctx, jwtStr)\nif err != nil {\n    var cause string\n    if pkgErr, ok := err.(interface{ Cause() error }); ok { cause = pkgErr.Cause().Error() }\n    log.Printf(\"JWT rejected: %v (cause: %s)\", err, cause)\n    return nil, status.Error(codes.Unauthenticated, \"invalid or expired token\")\n}","preventionTips":["Always log the wrapped cause, not just the outer message","Check token expiry client-side before sending requests","Sync server clocks with NTP","Return 401 with a stable reason code to callers"],"tags":["jwt","parsing","authentication"],"backgroundTag":"jwt-parse-failed","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}