{"record":{"id":"4a0ab89bbc50d3ad","repo":"AlistGo/alist","slug":"token-not-active-yet","errorCode":null,"errorMessage":"token not active yet","messagePattern":"token not active yet","errorType":"http","errorClass":null,"httpStatus":401,"severity":"warning","filePath":"server/common/auth.go","lineNumber":55,"sourceCode":"\tvalidTokenCache.Set(tokenString, true)\n\treturn tokenString, err\n}\n\nfunc ParseToken(tokenString string) (*UserClaims, error) {\n\ttoken, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {\n\t\treturn SecretKey, nil\n\t})\n\tif IsTokenInvalidated(tokenString) {\n\t\treturn nil, errors.New(\"token is invalidated\")\n\t}\n\tif err != nil {\n\t\tif ve, ok := err.(*jwt.ValidationError); ok {\n\t\t\tif ve.Errors&jwt.ValidationErrorMalformed != 0 {\n\t\t\t\treturn nil, errors.New(\"that's not even a token\")\n\t\t\t} else if ve.Errors&jwt.ValidationErrorExpired != 0 {\n\t\t\t\treturn nil, errors.New(\"token is expired\")\n\t\t\t} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {\n\t\t\t\treturn nil, errors.New(\"token not active yet\")\n\t\t\t} else {\n\t\t\t\treturn nil, errors.New(\"couldn't handle this token\")\n\t\t\t}\n\t\t}\n\t}\n\tif claims, ok := token.Claims.(*UserClaims); ok && token.Valid {\n\t\treturn claims, nil\n\t}\n\treturn nil, errors.New(\"couldn't handle this token\")\n}\n\nfunc InvalidateToken(tokenString string) error {\n\tif tokenString == \"\" {\n\t\treturn nil // don't invalidate empty guest token\n\t}\n\tvalidTokenCache.Del(tokenString)\n\treturn nil\n}","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/common/auth.go#L37-L73","documentation":"ParseToken maps jwt.ValidationErrorNotValidYet to this message: the token's nbf (NotBefore) claim is in the future, so the token is not yet acceptable. Tokens minted by GenerateToken set nbf = now, so this normally indicates clock skew or a manipulated token.","triggerScenarios":"Verifying a token on a server whose clock is behind the issuing server by more than the leeway, or using a hand-crafted token with a future nbf.","commonSituations":"NTP drift between nodes in multi-instance deployments; container host clock wrong; token generated on a machine with a future clock.","solutions":["Sync clocks (NTP/chrony) on all servers that issue or verify tokens","If skew is unavoidable, add validation leeway when calling jwt.ParseWithClaims so small nbf differences are tolerated","Regenerate the token after clocks are corrected"],"exampleFix":"// before\ntoken, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, kf) // no leeway\n\n// after\ntoken, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, kf,\n    jwt.WithLeeway(30*time.Second))","handlingStrategy":"validation","validationCode":"if time.Until(time.Unix(claims.NotBefore.Unix(), 0)) > 2*time.Minute {\n\t// clock skew or forged nbf; reject before JWT parse\n}","typeGuard":null,"tryCatchPattern":"_, err := common.ParseToken(tok)\nif err != nil && strings.Contains(err.Error(), \"not active yet\") {\n\t// check NTP sync, then ask client to retry shortly\n}","preventionTips":["Run NTP on all token-issuing and token-verifying hosts","Add leeway to JWT parsing when small skew is expected"],"tags":["jwt","auth","clock-skew","token"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}