dgraph-io/dgraph · error
Token is expired
Error message
Token is expired
What it means
errTokenExpired is a sentinel error returned by ParseJWT when the JWT library rejects a token because its exp claim is in the past. It signals the credential is structurally valid but no longer usable, so the caller must obtain a fresh token and retry rather than treating it as a configuration error.
Source
Thrown at x/jwt_helper.go:17
/*
* SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc.
* SPDX-License-Identifier: Apache-2.0
*/
package x
import (
"context"
"fmt"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
)
var (
errTokenExpired = errors.New("Token is expired")
)
// MaybeKeyToBytes converts the x.Sensitive type into []byte if the type of interface really
// is x.Sensitive. We keep the type x.Sensitive for private and public keys so that it
// doesn't get printed into the logs but the type the JWT library needs is []byte.
func MaybeKeyToBytes(k interface{}) interface{} {
if kb, ok := k.(Sensitive); ok {
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() {View on GitHub (pinned to 759e242be6)
Solutions
- Refresh the access JWT: re-login against /login (or the ACL refresh token flow) and retry the request with the new token
- Implement automatic retry-on-expired using the refresh token before re-authenticating fully
- Sync server clocks (NTP) if tokens appear expired while still fresh
- Increase token TTL if tokens legitimately expire too quickly for your workload
Example fix
// before
tok, err := x.ParseJWT(jwtStr, key) // err == errTokenExpired
// after
tok, err := x.ParseJWT(jwtStr, key)
if errors.Is(err, x.errTokenExpired) {
jwtStr = refreshAccessJWT(refreshToken) // re-login / refresh token flow
tok, err = x.ParseJWT(jwtStr, key)
} Defensive patterns
Strategy: try-catch
Validate before calling
claims := jwt.MapClaims{}
if _, err := jwt.ParseWithClaims(jwtStr, claims, keyFunc); err == nil {
if exp, ok := claims["exp"].(float64); ok && time.Now().After(time.Unix(int64(exp), 0)) {
jwtStr = refreshAccessToken(refreshToken) // refresh before use
}
} Type guard
func tokenExpired(jwtStr string, key []byte) bool {
_, err := x.ParseJWT(jwtStr, key)
return err != nil && strings.Contains(err.Error(), "expired")
} Try / catch
tok, err := x.ParseJWT(jwtStr, key)
if err != nil && errors.Is(err, x.errTokenExpired) {
jwtStr = refreshOrRelogin(refreshToken)
tok, err = x.ParseJWT(jwtStr, key) // retry once with fresh token
} Prevention
- Store refresh tokens and refresh the access JWT proactively before expiry
- Treat expiry as retryable: refresh once and re-issue the request, don't fail the operation
- Keep verifier and issuer clocks synced via NTP
- Track exp claims client-side and refresh when within a safety margin (e.g. 30s)
When it happens
Trigger: Calling ParseJWT with a JWT whose exp timestamp has passed; long-lived processes caching an access token and using it after expiry; clock skew between issuer and verifier making a token appear expired.
Common situations: Dgraph ACL access JWTs used past their validity window; clients that refreshed credentials only at process start; server clocks out of sync (NTP drift); tokens minted with very short TTLs.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ACL is disabled
- Authorize guardian of the galaxy, extracting jwt token, erro
- expecting either JWKUrl or JWKUrls, both were given
- expecting either JWKUrl/JWKUrls or (VerificationKey, Algo),
- required field missing in Dgraph.Authorization:%s
AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01).
Data as JSON: /api/errors/b83e31f27d0a0067.
Report an issue: GitHub.