{"record":{"id":"6c13e758e91a6e3c","repo":"AlistGo/alist","slug":"couldn-t-handle-this-token","errorCode":null,"errorMessage":"couldn't handle this token","messagePattern":"couldn't handle this token","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"server/common/auth.go","lineNumber":57,"sourceCode":"}\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}\n\nfunc IsTokenInvalidated(tokenString string) bool {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/common/auth.go#L39-L75","documentation":"ParseToken's fallback inside the ValidationError branch: the JWT library reported a validation error that is neither Malformed, Expired, nor NotValidYet — typically a signature validation failure (ValidationErrorSignatureInvalid) from signing with a different key.","triggerScenarios":"Verifying a token signed with a different SecretKey: alist restarted with a regenerated secret, a token from another instance/deployment, or a hand-forged signature.","commonSituations":"alist generates a random JWT secret on first run; wiping data or changing the secret invalidates all outstanding tokens. Multi-instance setups where instances do not share the secret.","solutions":["Re-login to obtain a token signed by the current instance's key","Pin the JWT secret in config so restarts do not rotate it","Share the same secret across all instances that must verify each other's tokens"],"exampleFix":"# before (config.json)\n\"jwt_secret\": \"\"  # regenerated each deploy -> old tokens fail\n\n# after\n\"jwt_secret\": \"a-long-stable-random-value\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"_, err := common.ParseToken(tok)\nif err != nil && strings.Contains(err.Error(), \"couldn't handle this token\") {\n\tc.AbortWithStatusJSON(401, gin.H{\"error\": \"token rejected\"})\n\treturn\n}","preventionTips":["Pin the jwt_secret in config so restarts do not rotate it","Share one secret across instances that verify each other's tokens"],"tags":["jwt","auth","signature","secret","token"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}