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

  1. Check the course's workflow_state and grant rights before sending; skip or surface 'course concluded' in the UI
  2. Rescue ConversationsHelper::Error with status :unauthorized in the caller and notify the user
  3. Reopen the course (un-conclude) if messaging should continue
  4. 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

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


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)