instructure/canvas-lms · warning · ConversationsHelper::Error
Course concluded, unable to send messages
Error message
Course concluded, unable to send messages
What it means
process_response rejects sending messages into a Course whose workflow_state is 'completed' unless the user has :read_as_admin rights, raising ConversationsHelper::Error with status :unauthorized. This enforces the Canvas rule that concluded courses no longer accept student conversation traffic.
Solutions
- Check the course's workflow_state and grant rights before sending; skip or surface 'course concluded' in the UI
- Rescue ConversationsHelper::Error with status :unauthorized in the caller and notify the user
- Reopen the course (un-conclude) if messaging should continue
- Perform the action as a user with :read_as_admin rights if appropriate
Example fix
// before
process_response(...)
// after
if context.is_a?(Course) && context.workflow_state == 'completed' && !context.grants_right?(current_user, session, :read_as_admin)
render json: { error: 'course concluded' }, status: :unauthorized
else
process_response(...)
end Defensive patterns
Strategy: validation
Validate before calling
if context.is_a?(Course) && context.workflow_state == 'completed' && !context.grants_right?(current_user, session, :read_as_admin)
return render json: { error: 'course concluded' }, status: :unauthorized
end Try / catch
begin
process_response(...)
rescue ConversationsHelper::Error => e
render json: { error: e.message }, status: e.status
end Prevention
- Disable/annotate conversation UI for concluded courses
- Re-check course state server-side on every send
- Rescue ConversationsHelper::Error in automation paths
When it happens
Trigger: Calling process_response/trigger_out_of_office_auto_responses with context being a Course with workflow_state == 'completed' and a current_user lacking :read_as_admin (i.e. a student or non-admin participant).
Common situations: Sending conversation messages shortly after course conclusion; automated out-of-office replies processed after a course wrapped up; a user with a stale UI session replying post-conclusion.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Unauthorized, unable to add messages to conversation
- can only forward one conversation at a time
- can't add participants if there are no messages
- can't add participants to a private conversation
- feature flag is disabled
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/ba0c33da4041cb0f.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/conversations_helper.rb:41
context:,
current_user:,
session:,
recipients:,
context_code:,
message_ids:,
body:,
attachment_ids:,
domain_root_account_id:,
media_comment_id:,
media_comment_type:,
automated: false
)
if conversation.conversation.replies_locked_for?(current_user, recipients)
raise ConversationsHelper::RepliesLockedForUser.new(message: I18n.t("Unauthorized, unable to add messages to conversation"), status: :unauthorized, attribute: "workflow_state")
end
if context.is_a?(Course) && context.workflow_state == "completed" && !context.grants_right?(current_user, session, :read_as_admin)
raise ConversationsHelper::Error.new(message: I18n.t("Course concluded, unable to send messages"), status: :unauthorized, attribute: "workflow_state")
end
if body.blank?
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
View on GitHub (pinned to 1c9f0bb801)