{"record":{"id":"ddb13fe74b1a7ac8","repo":"instructure/canvas-lms","slug":"user-doesn-t-have-permission-to-forward-these-messages","errorCode":null,"errorMessage":"user doesn't have permission to forward these messages","messagePattern":"user doesn't have permission to forward these messages","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/conversation.rb","lineNumber":356,"sourceCode":"  def self.build_message(current_user, body, options = {})\n    message = ConversationMessage.new\n    message.author_id = current_user.id\n    message.body = body\n    message.generated = options[:generated] || false\n    message.automated = options[:automated] || false\n    if options[:root_account_id]\n      message.context_type = \"Account\"\n      message.context_id = options[:root_account_id]\n    end\n\n    message.asset = options[:asset]\n    message.attachment_ids = options[:attachment_ids] if options[:attachment_ids].present?\n    message.media_comment = options[:media_comment] if options[:media_comment].present?\n    if options[:forwarded_message_ids].present?\n      messages = ConversationMessage.where(id: options[:forwarded_message_ids].map(&:to_i))\n      conversation_ids = messages.select(&:forwardable?).map(&:conversation_id).uniq\n      raise \"can only forward one conversation at a time\" if conversation_ids.size != 1\n      raise \"user doesn't have permission to forward these messages\" unless current_user.all_conversations.where(conversation_id: conversation_ids.first).exists?\n\n      # TODO: optimize me\n      message.forwarded_message_ids = messages.map(&:id).join(\",\")\n    end\n\n    # Grab snapshot hash of user's inbox settings and save to message (If FF is enabled)\n    if Account.site_admin.feature_enabled?(:inbox_settings)\n      message.inbox_settings_ooo_hash = Inbox::InboxService.inbox_settings_ooo_hash(user_id: current_user.id, root_account_id: options[:root_account_id])\n    end\n\n    message\n  end\n\n  def preload_users_and_context_codes\n    users = User.where(id: conversation_participants.map(&:user_id)).pluck(:id, :updated_at).map do |id, updated_at|\n      User.send(:instantiate, \"id\" => id, \"updated_at\" => updated_at)\n    end\n    User.preload_conversation_context_codes(users)","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/conversation.rb#L338-L374","documentation":"After confirming forwarded messages belong to one conversation, the code checks the current user is actually a participant of that conversation via their all_conversations. If not, this error is raised to stop users forwarding conversations they are not in.","triggerScenarios":"Calling Conversation#add_message with forwarded_message_ids whose (single) conversation the current user has no ConversationParticipant record for — e.g. forwarding someone else's messages, or after being removed from the conversation.","commonSituations":"Acting on behalf of another user (masquerading or passing the wrong user), stale UI state where the user left/deleted the conversation, or scripts using admin users who are not participants.","solutions":["Verify current_user is a participant of the source conversation before forwarding","Pass the correct user (the participant) rather than an admin/other user","If admin forwarding is required, first check user.all_conversations.exists?(conversation_id: cid) and handle gracefully"],"exampleFix":"// before\nconversation.add_message(admin, 'fyi', forwarded_message_ids: ids)\n// after\nraise 'not a participant' unless participant_user.all_conversations.where(conversation_id: source_cid).exists?\nconversation.add_message(participant_user, 'fyi', forwarded_message_ids: ids)","handlingStrategy":"validation","validationCode":"cid = ConversationMessage.find(id).conversation_id\nraise 'not a participant' unless user.all_conversations.where(conversation_id: cid).exists?","typeGuard":"null","tryCatchPattern":"begin\n  conversation.add_message(user, text, forwarded_message_ids: ids)\nrescue RuntimeError => e\n  raise unless e.message == \"user doesn't have permission to forward these messages\"\n  # surface 'you are not a participant of this conversation' to the user\nend","preventionTips":["Always pass the participant user, not an admin, to add_message","Check participant membership before enabling forward UI actions","Re-check membership if the user may have left the conversation since page load"],"tags":["ruby","rails","conversations","permissions"],"backgroundTag":"permission-denied","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}