dgraph-io/dgraph · error
token contains an invalid JSON Web Token: Token is expired
Error message
token contains an invalid JSON Web Token: Token is expired
What it means
When jwt.Parse fails because the token's exp claim is in the past, ParseJWT rewraps it with errTokenExpired and jwt.ErrTokenInvalidClaims ('token contains an invalid JWT: Token is expired') for backward compatibility. It means the JWT is structurally fine but its lifetime has ended, so it must be refreshed.
Source
Thrown at x/jwt_helper.go:43
return []byte(kb)
}
return k
}
func ParseJWT(jwtStr string) (jwt.MapClaims, error) {
token, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) {
if WorkerConfig.AclJwtAlg == nil {
return nil, errors.Errorf("ACL is disabled")
}
if token.Method.Alg() != WorkerConfig.AclJwtAlg.Alg() {
return nil, errors.Errorf("unexpected signing method in token: %v", token.Header["alg"])
}
return MaybeKeyToBytes(WorkerConfig.AclPublicKey), nil
})
if err != nil {
// This is for backward compatibility in clients
if errors.Is(err, jwt.ErrTokenExpired) {
err = errors.Wrap(errTokenExpired, jwt.ErrTokenInvalidClaims.Error())
}
return nil, errors.Wrapf(err, "unable to parse jwt token")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
return nil, errors.Errorf("claims in jwt token is not map claims")
}
return claims, nil
}
func ExtractUserName(jwtToken string) (string, error) {
claims, err := ParseJWT(jwtToken)
if err != nil {
return "", err
}
userId, ok := claims["userid"].(string)
if !ok {View on GitHub (pinned to 759e242be6)
Solutions
- Obtain a fresh access JWT (e.g. via refresh token / Login API) and retry the request.
- Reduce client TTL assumptions: re-login or refresh before/at expiry instead of caching tokens indefinitely.
- Check clock synchronization (NTP) if the token looks freshly minted yet is reported expired.
- If the token should still be valid, inspect its exp claim (jwt.io / decoder) to confirm actual expiry.
Example fix
// before: reuse cached token forever
token := cachedToken
// after: refresh when expired
if claims, err := x.ParseJWT(token); err != nil && errors.Is(err, jwt.ErrTokenExpired) {
token = refreshToken(cfg.RefreshJwt)
} Defensive patterns
Strategy: retry
Validate before calling
claims := jwt.MapClaims{}
if _, _, err := jwt.NewParser().ParseUnverified(token, claims); err == nil {
if exp, err2 := claims.GetExpirationTime(); err2 == nil && exp != nil && exp.Before(time.Now()) {
token = refreshAccessToken() // fetch new token before calling the API
}
} Type guard
func isExpiredJwtErr(err error) bool { return err != nil && errors.Is(err, jwt.ErrTokenExpired) } Try / catch
claims, err := x.ParseJWT(token)
if errors.Is(err, jwt.ErrTokenExpired) { // matches wrapped errTokenExpired
token = refreshToken()
claims, err = x.ParseJWT(token)
} Prevention
- Track exp claim client-side and refresh proactively (e.g. at 80% of TTL).
- Use refresh tokens to obtain new access tokens automatically.
- Synchronize clocks with NTP on all nodes.
- Do not cache access JWTs beyond their access-ttl.
When it happens
Trigger: Calling ParseJWT/ExtractUserName/ExtractNamespaceFromJwt with an access JWT whose exp claim is earlier than the current time (errors.Is(err, jwt.ErrTokenExpired) is true).
Common situations: Long-running client processes caching an access JWT past its TTL (default access-ttl); system clock skew between client and server; tokens minted long ago and replayed after server restarts.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- expecting either JWKUrl or JWKUrls, both were given
- expecting either JWKUrl/JWKUrls or (VerificationKey, Algo),
- required field missing in Dgraph.Authorization:%s
- invalid Bearer-formatted header value for JWT (%s)
- audience value was expected but not provided
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/fe367735752e9112.
Report an issue: GitHub.