{"record":{"id":"fef8e9caa7cfb4af","repo":"Tencent/WeKnora","slug":"request-has-already-been-reviewed","errorCode":null,"errorMessage":"request has already been reviewed","messagePattern":"request has already been reviewed","errorType":"http","errorClass":null,"httpStatus":409,"severity":"warning","filePath":"internal/application/service/organization.go","lineNumber":704,"sourceCode":"// CountPendingJoinRequests returns the number of pending join requests for an organization\nfunc (s *organizationService) CountPendingJoinRequests(ctx context.Context, orgID string) (int64, error) {\n\treturn s.orgRepo.CountJoinRequests(ctx, orgID, types.JoinRequestStatusPending)\n}\n\n// ReviewJoinRequest reviews a join request or upgrade request (approve or reject).\n// On approve the targeted tenant gets the assigned role; reviewerTenantID is\n// only used for audit (the gate is the route-level Admin guard).\nfunc (s *organizationService) ReviewJoinRequest(ctx context.Context, orgID string, requestID string, approved bool, reviewerID string, reviewerTenantID uint64, message string, assignRole *types.OrgMemberRole) error {\n\trequest, err := s.orgRepo.GetJoinRequestByID(ctx, requestID)\n\tif err != nil {\n\t\treturn ErrJoinRequestNotFound\n\t}\n\tif request.OrganizationID != orgID {\n\t\treturn ErrJoinRequestNotFound\n\t}\n\n\tif request.Status != types.JoinRequestStatusPending {\n\t\treturn errors.New(\"request has already been reviewed\")\n\t}\n\n\tvar status types.JoinRequestStatus\n\tif approved {\n\t\tstatus = types.JoinRequestStatusApproved\n\n\t\trole := types.OrgRoleViewer\n\t\tif assignRole != nil && assignRole.IsValid() {\n\t\t\trole = *assignRole\n\t\t} else if request.RequestedRole != \"\" && request.RequestedRole.IsValid() {\n\t\t\trole = request.RequestedRole\n\t\t}\n\n\t\tif request.RequestType == types.JoinRequestTypeUpgrade {\n\t\t\tif err := s.orgRepo.UpdateTenantMemberRole(ctx, request.OrganizationID, request.TenantID, role); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlogger.Infof(ctx, \"Upgrade request %s approved, tenant %d role updated to %s in organization %s\", requestID, request.TenantID, role, request.OrganizationID)","sourceCodeStart":686,"sourceCodeEnd":722,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/organization.go#L686-L722","documentation":"ReviewJoinRequest returns this inline error when the target join request exists and belongs to the organization but its Status is not JoinRequestStatusPending — i.e. it was already approved or rejected. This prevents double-processing of a review decision.","triggerScenarios":"Two admins clicking approve/reject concurrently, or a reviewer retrying after the first click succeeded but the UI did not update; also stale review pages listing already-decided requests.","commonSituations":"Double-click submissions, review queues cached beyond the decision moment, or API retries (at-least-once delivery) replaying an approval.","solutions":["Treat the error as idempotent: fetch the request and check its current status before surfacing a failure.","Disable review actions in the UI once a request is no longer pending.","Handle the message match (or add a sentinel) so retries don't report false failures."],"exampleFix":"// before\nerr := svc.ReviewJoinRequest(ctx, orgID, requestID, true)\nif err != nil { return err }\n// after\nerr := svc.ReviewJoinRequest(ctx, orgID, requestID, true)\nif err != nil && strings.Contains(err.Error(), \"already been reviewed\") {\n    return nil // decision already recorded\n} else if err != nil { return err }","handlingStrategy":"try-catch","validationCode":"req, err := svc.GetJoinRequestByID(ctx, orgID, requestID)\nif err == nil && req.Status != types.JoinRequestStatusPending {\n    return fmt.Errorf(\"request already %s\", req.Status)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"already been reviewed\") {\n    return nil // another admin already decided; treat as success\n}","preventionTips":["Disable review buttons for non-pending requests.","Fetch current status before submitting a review.","Design review calls as idempotent operations."],"tags":["state-conflict","idempotency","organization","go"],"backgroundTag":"already-processed-conflict","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}