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

Insufficient permissions

Error message

Insufficient permissions

What it means

This GraphQL mutation raises "Insufficient permissions" when the current user does not have the :delete right on the target DiscussionTopic. The record is found and readable (it passed the earlier find/read check), but delete authorization fails via grants_right?. This is Canvas's permission model enforcing that only users with delete rights (e.g. teachers, admins) can destroy a discussion topic.

Solutions

  1. Verify the current_user grants_right?(topic.context, :delete) before calling the mutation
  2. Use an account admin token or a user with teacher role in the topic's course
  3. Check enrollment roles/permissions in the course admin UI
  4. Confirm the mutation is passed the correct topic id for the intended course

Example fix

// before
record.destroy # raises if user lacks :delete
// after
unless record.grants_right?(current_user, nil, :delete)
  raise GraphQL::ExecutionError, "Insufficient permissions"
end
record.destroy
Defensive patterns

Strategy: try-catch

Validate before calling

// caller-side pre-check via a GraphQL query
const perms = await query(topicPermissions, { id });
if (!perms?.delete) throw new SkipMutation();

Type guard

function canDeleteTopic(topic) {
  return topic != null && topic.permissions?.delete === true;
}

Try / catch

try {
  await client.mutate(DELETE_DISCUSSION_TOPIC, { id });
} catch (e) {
  if (e.message === "Insufficient permissions") {
    notifyUser("You do not have permission to delete this discussion topic.");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the deleteDiscussionTopic mutation with a valid, readable topic id while the current_user lacks :delete on that topic's context — e.g. a student calling it on a course discussion, or a teacher on a topic in a course where they only have TA/read rights.

Common situations: Scripts using a non-admin token, users enrolled read-only in a course, topics locked to student deletion, or cross-course authorization confusion where the caller assumed course-level rights apply.

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/23cdd73353c948bc. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/delete_discussion_topic.rb:33

# 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::DeleteDiscussionTopic < Mutations::BaseMutation
  graphql_name "DeleteDiscussionTopic"
  # input arguments
  argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("DiscussionTopic")

  # the return data if the delete is successful
  field :discussion_topic_id, ID, null: false

  def resolve(input:)
    record = DiscussionTopic.active.find_by(id: input[:id])
    raise GraphQL::ExecutionError, "Unable to find Discussion Topic" if record.nil? || !record.grants_right?(current_user, nil, :read)

    unless record.grants_right?(current_user, nil, :delete)
      raise GraphQL::ExecutionError, "Insufficient permissions"
    end

    context[:deleted_models] = { discussion_topic: {} }
    context[:deleted_models][:discussion_topic] = record
    record.destroy
    {
      discussion_topic_id: record.id
    }
  end

  def self.discussion_topic_id_log_entry(_topic, context)
    context[:deleted_models][:discussion_topic]
  end
end

View on GitHub (pinned to 1c9f0bb801)