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

insufficient permissions

Error message

insufficient permissions

What it means

When the input includes rating, resolve checks discussion_entry.grants_right?(current_user, session, :rate) and raises GraphQL::ExecutionError 'insufficient permissions' before calling change_rating. Rating (liking) entries is restricted, so the mutation refuses to apply the rating for users without the :rate right.

Solutions

  1. Enable/allow rating on the discussion topic (or permission) for the user's role
  2. Send read-state changes without the rating field if the user cannot rate
  3. Authenticate as a user with the rate right in that course
  4. Check entry.grants_right?(user, session, :rate) client-side before including rating

Example fix

// before
mutation { updateDiscussionEntryParticipant(input: {discussionEntryId: "7", rating: 1}) { ... } } // user cannot rate
// after
mutation { updateDiscussionEntryParticipant(input: {discussionEntryId: "7", read: true}) { ... } } // omit rating, or grant rate permission
Defensive patterns

Strategy: try-catch

Validate before calling

const perms = await canvasQuery(`query { discussionTopic(id: $tid) { permissions { rate { enabled } } } }`, {tid});
if (!perms?.discussionTopic?.permissions?.rate?.enabled) throw new Error('user cannot rate this entry; omit rating from input');

Type guard

function canRate(perms) { return perms?.discussionTopic?.permissions?.rate?.enabled === true; }

Try / catch

try {
  await updateDiscussionEntryParticipant({ discussionEntryId: id, rating: 1 });
} catch (e) {
  if (/insufficient permissions/.test(e.message)) {
    // retry without the rating field
  } else { throw e; }
}

Prevention

When it happens

Trigger: Passing a non-nil rating while the current user lacks the :rate right — e.g. the discussion topic has grading/allow_rating disabled or is limited, the user is not enrolled in the course, or the entry belongs to a topic where ratings are teacher-only.

Common situations: Rating in a discussion where allow_rating is turned off or restricted by course settings; non-student roles without rate permission; clients sending rating alongside read-state updates in one mutation.

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/0182f0f4f3648a02. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/update_discussion_entry_participant.rb:56

  argument :discussion_entry_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func("DiscussionEntry")
  argument :forced_read_state, Boolean, required: false
  argument :rating, Types::RatingInputType, required: false
  argument :read, Boolean, required: false
  argument :report_type, Types::ReportType, required: false

  field :discussion_entry, Types::DiscussionEntryType, null: false
  def resolve(input:)
    discussion_entry = DiscussionEntry.find(input[:discussion_entry_id])
    raise GraphQL::ExecutionError, "not found" unless discussion_entry.grants_right?(current_user, session, :read)

    unless input[:read].nil?
      opt = input[:forced_read_state].nil? ? {} : { forced: input[:forced_read_state] }
      discussion_entry.change_read_state(input[:read] ? "read" : "unread", current_user, opt)
    end

    unless input[:rating].nil?
      raise GraphQL::ExecutionError, "insufficient permissions" unless discussion_entry.grants_right?(current_user, session, :rate)

      discussion_entry.change_rating(input[:rating], current_user)
    end

    unless input[:report_type].nil?
      InstStatsd::Statsd.distributed_increment("discussion_entry_participant.report.created")
      discussion_entry.change_report_type(input[:report_type], current_user)
    end

    # TODO: VICE-1321
    # need to reload entry record as we currently return stale data
    {
      discussion_entry: discussion_entry.reload
    }
  rescue ActiveRecord::RecordNotFound
    raise GraphQL::ExecutionError, "not found"
  end
end

View on GitHub (pinned to 1c9f0bb801)