{"record":{"id":"0d3d00a967e189b7","repo":"forem/forem","slug":"you-cannot-merge-the-same-two-user-ids","errorCode":null,"errorMessage":"You cannot merge the same two user IDs","messagePattern":"You cannot merge the same two user IDs","errorType":"exception","errorClass":"StandardError","httpStatus":null,"severity":"error","filePath":"app/services/moderator/merge_user.rb","lineNumber":16,"sourceCode":"module Moderator\n  class MergeUser < ManageActivityAndRoles\n    def self.call(admin:, keep_user:, delete_user_id:)\n      new(keep_user: keep_user, admin: admin, delete_user_id: delete_user_id).merge\n    end\n\n    attr_reader :keep_user, :admin, :delete_user_id\n\n    def initialize(admin:, keep_user:, delete_user_id:)\n      @keep_user = keep_user\n      @admin = admin\n      @delete_user = User.find(delete_user_id.to_i)\n    end\n\n    def merge\n      raise StandardError, I18n.t(\"services.moderator.merge_user.same_user\") if @delete_user.id == @keep_user.id\n\n      handle_identities\n      merge_content\n      merge_follows\n      merge_mentions\n      merge_profile\n      update_social\n      # The merged-away row is deleted, but this is a merge, not a GDPR\n      # erasure — MLH Core merges the two accounts instead of erasing one.\n      Users::DeleteWorker.new.perform(@delete_user.id, true, \"merge\")\n      @keep_user.touch(:profile_updated_at)\n      @keep_user.track!(\"user_merged\", { \"merged_forem_user_id\" => @delete_user.id })\n      Users::MergeSyncWorker.perform_async(@keep_user.id)\n\n      EdgeCache::Bust.call(\"/#{@keep_user.username}\")\n    end\n\n    private","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/forem/forem/blob/f354c376a7c5d1330dc40d66f150be8d1289020d/app/services/moderator/merge_user.rb#L1-L34","documentation":"Moderator::MergeUsers service raises StandardError ('You cannot merge the same two user IDs') when the user to be deleted (@delete_user, looked up from delete_user_id) has the same id as keep_user. The guard exists because merging an account into itself would run identity/content merges against the same row and then send it through Users::DeleteWorker, destroying the surviving account.","triggerScenarios":"An admin submits the user-merge form (or calls Moderator::MergeUsers.call) with the same user id in both the 'keep' and 'delete' fields; also when delete_user_id is passed as a string that to_i's to the same id.","commonSituations":"Copy-paste error in the /admin merge UI filling both inputs with one id; scripts that loop over user pairs and hit a self-pair; support workflows where the two disputed accounts turned out to be one account.","solutions":["Double-check the two IDs are distinct before submitting the merge (they are different accounts).","If you meant to merge, re-run with the correct delete_user_id.","Add a UI/caller-side precondition: raise/flash early when params[:keep_user_id] == params[:delete_user_id]."],"exampleFix":"# before\nModerator::MergeUsers.call(admin: admin, keep_user: user, delete_user_id: user.id)\n# raises 'You cannot merge the same two user IDs'\n\n# after\nif keep_user.id == delete_user_id.to_i\n  Rails.logger.info('Self-merge requested; nothing to do')\nelse\n  Moderator::MergeUsers.call(admin: admin, keep_user: keep_user, delete_user_id: delete_user_id)\nend","handlingStrategy":"validation","validationCode":"if keep_user.id == delete_user_id.to_i\n  render json: { error: 'Cannot merge a user into itself' }, status: :unprocessable_entity\nend","typeGuard":null,"tryCatchPattern":"rescue StandardError => e\n  flash[:error] = e.message # surfaces 'You cannot merge the same two user IDs'\n  redirect_to admin_merge_path\nend","preventionTips":["Validate distinct IDs in the merge UI before submit.","In scripted merges, skip self-pairs instead of relying on the service to raise.","Log both user handles for the audit trail when the guard fires."],"tags":["admin","user-management","merge","validation"],"backgroundTag":"user-merge-conflict","analyzedSha":"f354c376a7c5d1330dc40d66f150be8d1289020d","analyzedAt":"2026-08-21T12:43:44.428Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}