{"record":{"id":"0182f0f4f3648a02","repo":"instructure/canvas-lms","slug":"insufficient-permissions-update-discussion-entry-participant","errorCode":null,"errorMessage":"insufficient permissions","messagePattern":"insufficient permissions","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/update_discussion_entry_participant.rb","lineNumber":56,"sourceCode":"\n  argument :discussion_entry_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"DiscussionEntry\")\n  argument :forced_read_state, Boolean, required: false\n  argument :rating, Types::RatingInputType, required: false\n  argument :read, Boolean, required: false\n  argument :report_type, Types::ReportType, required: false\n\n  field :discussion_entry, Types::DiscussionEntryType, null: false\n  def resolve(input:)\n    discussion_entry = DiscussionEntry.find(input[:discussion_entry_id])\n    raise GraphQL::ExecutionError, \"not found\" unless discussion_entry.grants_right?(current_user, session, :read)\n\n    unless input[:read].nil?\n      opt = input[:forced_read_state].nil? ? {} : { forced: input[:forced_read_state] }\n      discussion_entry.change_read_state(input[:read] ? \"read\" : \"unread\", current_user, opt)\n    end\n\n    unless input[:rating].nil?\n      raise GraphQL::ExecutionError, \"insufficient permissions\" unless discussion_entry.grants_right?(current_user, session, :rate)\n\n      discussion_entry.change_rating(input[:rating], current_user)\n    end\n\n    unless input[:report_type].nil?\n      InstStatsd::Statsd.distributed_increment(\"discussion_entry_participant.report.created\")\n      discussion_entry.change_report_type(input[:report_type], current_user)\n    end\n\n    # TODO: VICE-1321\n    # need to reload entry record as we currently return stale data\n    {\n      discussion_entry: discussion_entry.reload\n    }\n  rescue ActiveRecord::RecordNotFound\n    raise GraphQL::ExecutionError, \"not found\"\n  end\nend","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/update_discussion_entry_participant.rb#L38-L74","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nmutation { updateDiscussionEntryParticipant(input: {discussionEntryId: \"7\", rating: 1}) { ... } } // user cannot rate\n// after\nmutation { updateDiscussionEntryParticipant(input: {discussionEntryId: \"7\", read: true}) { ... } } // omit rating, or grant rate permission","handlingStrategy":"try-catch","validationCode":"const perms = await canvasQuery(`query { discussionTopic(id: $tid) { permissions { rate { enabled } } } }`, {tid});\nif (!perms?.discussionTopic?.permissions?.rate?.enabled) throw new Error('user cannot rate this entry; omit rating from input');","typeGuard":"function canRate(perms) { return perms?.discussionTopic?.permissions?.rate?.enabled === true; }","tryCatchPattern":"try {\n  await updateDiscussionEntryParticipant({ discussionEntryId: id, rating: 1 });\n} catch (e) {\n  if (/insufficient permissions/.test(e.message)) {\n    // retry without the rating field\n  } else { throw e; }\n}","preventionTips":["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"],"tags":["graphql","permissions","rating"],"backgroundTag":"insufficient-permissions","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}