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
- Enable/allow rating on the discussion topic (or permission) for the user's role
- Send read-state changes without the rating field if the user cannot rate
- Authenticate as a user with the rate right in that course
- 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
- Only include rating in the mutation when the topic allows rating for the user's role
- Split read-state and rating into separate mutations so one permission failure does not block both
- Check topic settings (allow_rating / graded status) before enabling like UI
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
- ActiveRecord::RecordNotFound
- custom gradebook statuses feature flag is disabled
- feature flag is disabled
- insufficient permission
- insufficient permission
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
endView on GitHub (pinned to 1c9f0bb801)