{"record":{"id":"b83e31f27d0a0067","repo":"dgraph-io/dgraph","slug":"token-is-expired","errorCode":null,"errorMessage":"Token is expired","messagePattern":"Token is expired","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/jwt_helper.go","lineNumber":17,"sourceCode":"/*\n * SPDX-FileCopyrightText: © 2017-2026 Istari Digital, Inc.\n * SPDX-License-Identifier: Apache-2.0\n */\n\npackage x\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/golang-jwt/jwt/v5\"\n\t\"github.com/pkg/errors\"\n)\n\nvar (\n\terrTokenExpired = errors.New(\"Token is expired\")\n)\n\n// MaybeKeyToBytes converts the x.Sensitive type into []byte if the type of interface really\n// is x.Sensitive. We keep the type x.Sensitive for private and public keys so that it\n// doesn't get printed into the logs but the type the JWT library needs is []byte.\nfunc MaybeKeyToBytes(k interface{}) interface{} {\n\tif kb, ok := k.(Sensitive); ok {\n\t\treturn []byte(kb)\n\t}\n\treturn k\n}\n\nfunc ParseJWT(jwtStr string) (jwt.MapClaims, error) {\n\ttoken, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) {\n\t\tif WorkerConfig.AclJwtAlg == nil {\n\t\t\treturn nil, errors.Errorf(\"ACL is disabled\")\n\t\t}\n\t\tif token.Method.Alg() != WorkerConfig.AclJwtAlg.Alg() {","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/x/jwt_helper.go#L1-L35","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntok, err := x.ParseJWT(jwtStr, key) // err == errTokenExpired\n// after\ntok, err := x.ParseJWT(jwtStr, key)\nif errors.Is(err, x.errTokenExpired) {\n    jwtStr = refreshAccessJWT(refreshToken) // re-login / refresh token flow\n    tok, err = x.ParseJWT(jwtStr, key)\n}","handlingStrategy":"try-catch","validationCode":"claims := jwt.MapClaims{}\nif _, err := jwt.ParseWithClaims(jwtStr, claims, keyFunc); err == nil {\n    if exp, ok := claims[\"exp\"].(float64); ok && time.Now().After(time.Unix(int64(exp), 0)) {\n        jwtStr = refreshAccessToken(refreshToken) // refresh before use\n    }\n}","typeGuard":"func tokenExpired(jwtStr string, key []byte) bool {\n    _, err := x.ParseJWT(jwtStr, key)\n    return err != nil && strings.Contains(err.Error(), \"expired\")\n}","tryCatchPattern":"tok, err := x.ParseJWT(jwtStr, key)\nif err != nil && errors.Is(err, x.errTokenExpired) {\n    jwtStr = refreshOrRelogin(refreshToken)\n    tok, err = x.ParseJWT(jwtStr, key) // retry once with fresh token\n}","preventionTips":["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)"],"tags":["jwt","authentication","token-expiry","acl"],"backgroundTag":"jwt-token-expired","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}