instructure/canvas-lms · error · ConversationsHelper::Error
The following recipients have no active enrollment in the…
Error message
The following recipients have no active enrollment in the course, %{invalid_recipients}, unable to send messages What it means
ConversationsHelper raises this ConversationsHelper::Error when validating recipients for a course-context conversation: one or more recipients lack an active enrollment in the course. The guard exists to prevent messaging users who are not currently enrolled, leaking or failing on stale recipient lists.
Solutions
- Filter recipients to users with an active enrollment in the context before sending (e.g. course.enrollments.active.where(user_id:) map).
- Refresh the recipient list from the API (GET /api/v1/courses/:id/enrollments) instead of a cached roster.
- If recipients should be valid, fix enrollment data: re-run SIS import or reinstate the enrollment.
- Catch ConversationsHelper::Error and surface the invalid_recipients names to the sender so they can deselect them.
Example fix
// before @conversation = create_conversation(..., recipients: params[:recipients]) // after invalid = ConversationsHelper.get_invalid_recipients(course, params[:recipients], current_user) params[:recipients] = params[:recipients] - invalid.pluck(1) raise if params[:recipients].empty? @conversation = create_conversation(..., recipients: params[:recipients])
Defensive patterns
Strategy: validation
Validate before calling
active_ids = course.enrollments.active.pluck(:user_id)
bad = recipients - active_ids.map(&:to_s)
raise ArgumentError, "not enrolled: #{bad.join(',')}" if bad.any? Type guard
def active_enrolled?(course, user_id) course.enrollments.active.exists?(user_id: user_id) end
Try / catch
begin
send_conversation(...)
rescue ConversationsHelper::Error => e
render json: { errors: [{ attribute: e.attribute, message: e.message }] }, status: e.status
end Prevention
- Always resolve recipients from the live enrollment API, not cached rosters
- Re-check enrollments right before bulk sends
- Surface invalid recipient names to the UI instead of failing silently
When it happens
Trigger: POST to the conversations API with context = a course and recipients whose enrollments are completed, deleted, invited-but-not-active, or whose enrollment was conclude/soft-deleted after the recipient list was cached.
Common situations: Sending to a course section roster after term conclusion; messaging users imported via SIS whose enrollment sync has not run; stale client-side autocomplete lists; users who dropped the course between drafting and sending.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Invalid enrollment type: #
- Student ids are not in course
- Too many participants for group conversation
- Unable to create message without a body
- A course did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/1dae0321244f6e04.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/conversations_helper.rb:63
raise ConversationsHelper::Error.new(message: I18n.t("Unable to create message without a body"), status: :bad_request, attribute: "empty_message")
end
recipients = normalize_recipients(
recipients:,
context_code:,
conversation_id: conversation.conversation_id,
current_user:,
session:
)
if recipients && !conversation.conversation.can_add_participants?(recipients)
raise ConversationsHelper::Error.new(message: I18n.t("Too many participants for group conversation"), status: :bad_request, attribute: "recipients")
end
invalid_recipients = get_invalid_recipients(context, recipients, current_user)
unless invalid_recipients.to_a.empty?
invalid_recipients = invalid_recipients.pluck(1)
raise ConversationsHelper::Error.new(message: I18n.t("The following recipients have no active enrollment in the course, %{invalid_recipients}, unable to send messages", invalid_recipients:), status: :unauthorized, attribute: "recipients")
end
tags = infer_tags(
recipients: conversation.conversation.participants.pluck(:id),
context_code:
)
validate_message_ids(message_ids, conversation, current_user:)
message_args = build_message_args(
body:,
attachment_ids:,
domain_root_account_id:,
media_comment_id:,
media_comment_type:,
current_user:,
automated:
)
View on GitHub (pinned to 1c9f0bb801)