{"record":{"id":"2a2df9615f5ab835","repo":"Tencent/WeKnora","slug":"pending-request-already-exists","errorCode":null,"errorMessage":"pending request already exists","messagePattern":"pending request already exists","errorType":"http","errorClass":null,"httpStatus":409,"severity":"warning","filePath":"internal/application/service/organization.go","lineNumber":622,"sourceCode":"\tif org.OwnerTenantID == 0 {\n\t\treturn true\n\t}\n\treturn org.OwnerTenantID == tenantID\n}\n\n// generateInviteCode generates a random 16-character invite code\nfunc generateInviteCode() string {\n\tbytes := make([]byte, 8)\n\t_, _ = rand.Read(bytes)\n\treturn hex.EncodeToString(bytes)\n}\n\n// ----------------\n// Join Requests\n// ----------------\n\nvar (\n\tErrPendingRequestExists    = errors.New(\"pending request already exists\")\n\tErrJoinRequestNotFound     = errors.New(\"join request not found\")\n\tErrCannotUpgradeToSameRole = errors.New(\"cannot request upgrade to same or lower role\")\n\tErrAlreadyAdmin            = errors.New(\"tenant is already an admin\")\n)\n\n// SubmitJoinRequest submits a request for the caller's tenant to join an organization.\n// Dedup is now per-tenant: any user from a tenant already with a pending join\n// request is rejected (the same tenant can't queue two simultaneous joins).\nfunc (s *organizationService) SubmitJoinRequest(ctx context.Context, orgID string, userID string, tenantID uint64, message string, requestedRole types.OrgMemberRole) (*types.OrganizationJoinRequest, error) {\n\tlogger.Infof(ctx, \"Tenant %d (rep user %s) submitting join request for organization %s\", tenantID, userID, orgID)\n\n\texisting, err := s.orgRepo.GetPendingRequestByTenantAndType(ctx, orgID, tenantID, types.JoinRequestTypeJoin)\n\tif err == nil && existing != nil {\n\t\treturn nil, ErrPendingRequestExists\n\t}\n\n\torg, err := s.orgRepo.GetByID(ctx, orgID)\n\tif err != nil {","sourceCodeStart":604,"sourceCodeEnd":640,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/organization.go#L604-L640","documentation":"ErrPendingRequestExists is a package-level sentinel error indicating the tenant already has a pending request of the same type (join or role upgrade) with the organization. SubmitJoinRequest and RequestRoleUpgrade check via GetPendingRequestByTenantAndType and dedupe per-tenant: any user from that tenant triggers the rejection, not just the original requester.","triggerScenarios":"Calling SubmitJoinRequest when the tenant already has a pending JoinRequestTypeJoin, or RequestRoleUpgrade when a pending JoinRequestTypeUpgrade exists for the same org/tenant pair.","commonSituations":"A user clicking 'request to join' twice, a different member of the same tenant submitting a duplicate request before an admin reviews the first, or retrying after a UI timeout without knowing the first call succeeded.","solutions":["Wait for an admin to review the existing pending request before submitting a new one.","Check for a pending request first (query by org/tenant) and surface its status instead of re-submitting.","Handle errors.Is(err, ErrPendingRequestExists) as an idempotent success in the UI."],"exampleFix":"// before\nreq, err := svc.SubmitJoinRequest(ctx, orgID, userID, tenantID, msg, role)\nif err != nil { return err }\n// after\nreq, err := svc.SubmitJoinRequest(ctx, orgID, userID, tenantID, msg, role)\nif errors.Is(err, organization.ErrPendingRequestExists) {\n    return nil // already requested; treat as no-op\n} else if err != nil { return err }","handlingStrategy":"try-catch","validationCode":"// check first\nexisting, err := orgRepo.GetPendingRequestByTenantAndType(ctx, orgID, tenantID, reqType)\nif err == nil && existing != nil { return fmt.Errorf(\"pending request %s already exists\", existing.ID) }","typeGuard":null,"tryCatchPattern":"if errors.Is(err, organization.ErrPendingRequestExists) {\n    return nil // idempotent no-op\n}","preventionTips":["Always match with errors.Is against ErrPendingRequestExists, not string comparison.","Check for existing pending requests before submitting.","Debounce/duplicate-submit-guard the UI button."],"tags":["duplicate-request","idempotency","organization","go"],"backgroundTag":"duplicate-request-conflict","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}