{"record":{"id":"3300540fc3d04d90","repo":"instructure/canvas-lms","slug":"not-a-participant","errorCode":null,"errorMessage":"not a participant","messagePattern":"not a participant","errorType":"exception","errorClass":"ConversationsHelper::Error","httpStatus":400,"severity":"error","filePath":"app/helpers/conversations_helper.rb","lineNumber":144,"sourceCode":"        message.conversation_id = conversation.conversation_id\n        message.created_at = Time.now.utc\n        { message:, recipients_count: existing_participant_ids.size, status: :accepted }\n      end\n    elsif conversation.should_process_immediately?\n      message = conversation.process_new_message(message_args, recipients, message_ids, tags)\n      { message:, recipients_count: recipients ? recipients.count : 0, status: :ok }\n    else\n      conversation.delay(strand: \"add_message_#{conversation.global_conversation_id}\").process_new_message(message_args, recipients, message_ids, tags)\n      message = Conversation.build_message(*message_args)\n      message.id = 0\n      message.conversation_id = conversation.conversation_id\n      message.created_at = Time.now.utc\n      { message:, recipients_count: recipients ? recipients.count : 0, status: :accepted }\n    end\n  rescue ConversationsHelper::InvalidMessageForConversationError\n    raise ConversationsHelper::Error.new(message: I18n.t(\"not for this conversation\"), status: :bad_request, attribute: \"included_messages\")\n  rescue ConversationsHelper::InvalidMessageParticipantError\n    raise ConversationsHelper::Error.new(message: I18n.t(\"not a participant\"), status: :bad_request, attribute: \"included_messages\")\n  end\n\n  def contexts_for(audience, context_tags)\n    result = { courses: {}, groups: {} }\n    return result if audience.empty?\n\n    context_tags.each do |tag|\n      next unless tag =~ /\\A(course|group)_(\\d+)\\z/\n\n      result[:\"#{$1}s\"][$2.to_i] = []\n    end\n\n    if audience.size == 1 && include_private_conversation_enrollments\n      enrollments = Shard.partition_by_shard(result[:courses].keys) do |course_ids|\n        next unless audience.first.associated_shards.include?(Shard.current)\n\n        Enrollment.where(course_id: course_ids, user_id: audience.first.id, workflow_state: \"active\").select([:course_id, :type]).to_a\n      end","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/helpers/conversations_helper.rb#L126-L162","documentation":"Rescue translator: when an included_message references a message whose author/owner is not a participant of the conversation (from the caller's perspective), ConversationsHelper::InvalidMessageParticipantError is re-raised as a bad_request ConversationsHelper::Error with 'not a participant'.","triggerScenarios":"Conversations API call with included_messages pointing at a message the current user can access in another conversation context but who is not a conversation participant (e.g. observer/admin-visible message, or conversation the user was removed from).","commonSituations":"User removed from a group conversation still has cached message ids; forwarding a message received in a thread the user has since left; admin tools acting on behalf of a non-participant.","solutions":["Ensure the current user is an active participant of the conversation before including its messages.","Drop included_messages ids for conversations the user is not a participant of.","Catch ConversationsHelper::Error (attribute included_messages, message 'not a participant') and show a friendly message.","Re-open the conversation in the UI so the participant list refreshes, then retry."],"exampleFix":"// before\nincluded_messages: Message.where(id: params[:included_messages])\n// after\nincluded_messages: current_user.conversations.find(conversation.id)\n  .conversation_messages.where(id: params[:included_messages]).pluck(:id)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def participant?(conversation, user)\n  conversation.conversation_participants.exists?(user_id: user.id)\nend","tryCatchPattern":"begin\n  add_message(...)\nrescue ConversationsHelper::Error => e\n  if e.message == I18n.t('not a participant')\n    flash[:error] = t('you_are_no_longer_part_of_this_conversation')\n  end\nend","preventionTips":["Verify current user is an active participant before including messages","Drop cached message ids when the user leaves a conversation","Re-fetch conversation state on UI resume"],"tags":["conversations","bad-request","participation"],"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"}