{"record":{"id":"22c59ebbdf6af5bc","repo":"usememos/memos","slug":"pat-not-found","errorCode":null,"errorMessage":"PAT not found","messagePattern":"PAT not found","errorType":"exception","errorClass":null,"httpStatus":401,"severity":"warning","filePath":"store/db/mysql/user_setting.go","lineNumber":108,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpatsUserSetting := &storepb.PersonalAccessTokensUserSetting{}\n\tif err := protojsonUnmarshaler.Unmarshal([]byte(tokensJSON), patsUserSetting); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, pat := range patsUserSetting.Tokens {\n\t\tif pat.TokenHash == tokenHash {\n\t\t\treturn &store.PATQueryResult{\n\t\t\t\tUserID: userID,\n\t\t\t\tPAT:    pat,\n\t\t\t}, nil\n\t\t}\n\t}\n\n\treturn nil, errors.New(\"PAT not found\")\n}\n","sourceCodeStart":90,"sourceCodeEnd":110,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/db/mysql/user_setting.go#L90-L110","documentation":"The MySQL user_setting driver reads the USER_SETTING row holding the PAT list, unmarshals it, and scans tokens for a matching TokenHash. \"PAT not found\" means the hash simply is not in the stored list — either the token was revoked, belongs to another user, or the hash input differs (PATs are matched by hash, never plaintext).","triggerScenarios":"Authenticating with a PAT whose hash does not match any stored token for that user ID; using a revoked or rotated PAT; passing the raw token where a hash is expected.","commonSituations":"Expired/revoked tokens still configured in scripts; tokens created before a hashing-scheme change; multi-user instances where the userID lookup and token belong to different accounts.","solutions":["Regenerate the PAT in Settings > Access Tokens and update the client","Confirm the token is passed intact (no truncation by shells or editors)","Map this error to gRPC codes.NotFound / HTTP 401 at the API layer so clients refresh credentials"],"exampleFix":"// before\nresult, err := mysqlStore.FindPAT(ctx, userID, tokenHash) // used raw token\n// after\nresult, err := mysqlStore.FindPAT(ctx, userID, hashToken(rawToken))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := d.FindPAT(ctx, userID, tokenHash)\nif err != nil {\n    if strings.Contains(err.Error(), \"PAT not found\") {\n        return nil, status.Error(codes.Unauthenticated, \"invalid personal access token\")\n    }\n    return nil, status.Error(codes.Internal, \"failed to query PAT\")\n}","preventionTips":["Rotate PATs proactively and remove revoked ones from automation secrets","Map PAT lookup misses to 401 so clients can re-authenticate"],"tags":["database","mysql","pat","auth","not-found"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}