instructure/canvas-lms · error · GraphQL::ExecutionError

Insufficient permissions

Error message

Insufficient permissions

What it means

DeleteConversationMessages#resolve rejects the entire mutation when the root account has the restrict_student_access feature enabled and (per the check ordering) the caller is not permitted, raising "Insufficient permissions". The flag is intended to block students from destructive conversation operations like deleting messages.

Solutions

  1. Log in / act as a non-student role (teacher, admin) if deletion of messages is required.
  2. If deletion should be allowed, have an admin disable the restrict_student_access feature flag on the root account.
  3. Update the client to hide/disable message deletion UI when the flag is active to avoid the failed call.
  4. Confirm the current_user's root account matches the one where you expect the flag configured.

Example fix

// before
deleteConversationMessages(input: { ids }) // student, flag on -> error
// after
if (me.isStudent && account.featureFlags.restrict_student_access) {
  hideDeleteButton()
} else {
  deleteConversationMessages(input: { ids })
}
Defensive patterns

Strategy: validation

Validate before calling

if (me.isStudent && rootAccount.featureFlags?.restrict_student_access) {
  throw new SkipError('message deletion blocked by restrict_student_access')
}

Type guard

function canDeleteMessages(me, account) { return !(me.isStudent && account?.featureFlags?.includes('restrict_student_access')); }

Try / catch

try {
  await deleteConversationMessages({ ids })
} catch (e) {
  if (e.message === 'Insufficient permissions') {
    disableConversationDeleteUi();
  } else throw e;
}

Prevention

When it happens

Trigger: A student (or a session without the required role) calling deleteConversationMessages on a root account where restrict_student_access is enabled.

Common situations: Institutions that turn on restrict_student_access to lock down student messaging; K-12/self-service accounts; a client that previously worked before the institution enabled the flag; admins testing with a student masquerade off/on incorrectly.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — 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/7198c3d56594631d. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/delete_conversation_messages.rb:31

# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

class Mutations::DeleteConversationMessages < Mutations::BaseMutation
  graphql_name "DeleteConversationMessages"

  # input arguments
  argument :ids, [ID], required: true, prepare: GraphQLHelpers.relay_or_legacy_ids_prepare_func("ConversationMessage")

  field :conversation_message_ids, [ID], null: false

  def resolve(input:)
    if current_user.account.root_account.feature_enabled?(:restrict_student_access)
      raise GraphQL::ExecutionError, "Insufficient permissions"
    end

    messages = ConversationMessage.preload(:conversation).find(input[:ids])
    if messages.map(&:conversation).uniq.length > 1
      raise GraphQL::ExecutionError, "All ConversationMessages must exist within the same Conversation"
    end

    participant_record = current_user.all_conversations.find_by(conversation_id: messages.first.conversation.id)
    raise GraphQL::ExecutionError, "Insufficient permissions" if participant_record.nil?

    participant_record.remove_messages(*messages)
    context[:deleted_models] = { conversation_messages: {} }
    messages.each { |message| context[:deleted_models][:conversation_messages][message.id.to_s] = message }
    { conversation_message_ids: input[:ids] }
  rescue ActiveRecord::RecordInvalid => e
    errors_for(e.record)
  rescue ActiveRecord::RecordNotFound
    raise GraphQL::ExecutionError, "Unable to find ConversationMessage"

View on GitHub (pinned to 1c9f0bb801)