{"record":{"id":"82072834dc862555","repo":"instructure/canvas-lms","slug":"can-t-add-participants-if-there-are-no-messages","errorCode":null,"errorMessage":"can't add participants if there are no messages","messagePattern":"can't add participants if there are no messages","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/conversation.rb","lineNumber":187,"sourceCode":"    message = nil\n    shard.activate do\n      user_ids = users.map(&:id).uniq\n      raise \"can't add participants to a private conversation\" if private?\n\n      transaction do\n        lock!\n        user_ids -= conversation_participants.map(&:user_id)\n        next if user_ids.empty?\n\n        if options[:no_messages]\n          bulk_insert_options = {\n            workflow_state: \"unread\",\n            message_count: 0,\n            private_hash:\n          }\n        else\n          last_message_at = conversation_messages.human.first.try(:created_at)\n          raise \"can't add participants if there are no messages\" unless last_message_at\n\n          num_messages = conversation_messages.human.size\n\n          bulk_insert_options = {\n            workflow_state: \"unread\",\n            last_message_at:,\n            message_count: num_messages,\n            private_hash:\n          }\n        end\n\n        Shard.partition_by_shard(user_ids) do |shard_user_ids|\n          unless shard_user_ids.empty?\n            shard_user_ids.sort!\n            shard_user_ids.each_slice(1000) do |sliced_user_ids|\n              User.where(id: sliced_user_ids).update_all([\"unread_conversations_count = unread_conversations_count + 1, updated_at = ?\", Time.now.utc])\n            end\n          end","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/conversation.rb#L169-L205","documentation":"add_participants computes last_message_at from conversation_messages.human.first and raises if nil, because adding participants to a conversation with no human messages would produce participants with no valid last_message_at/basis.","triggerScenarios":"Calling add_participants on a conversation whose human (non-deleted/non-ignored) messages have all been deleted, or that only contains non-human messages, so conversation_messages.human.first is nil.","commonSituations":"All messages in the thread deleted before adding participants; data where every message was removed by cleanup or user deletion; importing/merging conversations left messageless.","solutions":["Ensure the conversation has at least one live (human) message before adding participants; otherwise create a new conversation with a message","Check conversation_messages.human.exists? before calling add_participants","If data corruption removed messages unexpectedly, restore messages or mark the conversation deleted"],"exampleFix":"# before\nconversation.add_participants(user, new_users)\n# after\nif conversation.conversation_messages.human.exists?\n  conversation.add_participants(user, new_users)\nelse\n  raise 'conversation has no messages to extend'\nend","handlingStrategy":"validation","validationCode":"raise 'conversation has no human messages' unless conversation.conversation_messages.human.exists?","typeGuard":"null","tryCatchPattern":"begin\n  conversation.add_participants(user, users)\nrescue RuntimeError => e\n  raise e unless e.message.include?('no messages')\n  # create a new conversation with an initial message instead\nend","preventionTips":["Verify at least one human message exists before adding participants","Prevent deletion of all messages in conversations that can still be extended","Handle message-purge jobs that can leave conversations messageless"],"tags":["ruby","conversations","messages"],"backgroundTag":"empty-result-set","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"}