{"record":{"id":"5496856cdd932fef","repo":"instructure/canvas-lms","slug":"insufficient-permissions","errorCode":null,"errorMessage":"Insufficient Permissions","messagePattern":"Insufficient Permissions","errorType":"exception","errorClass":"InsufficientPermissionsError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_discussion_entry.rb","lineNumber":93,"sourceCode":"\n    obj = { discussion_entry: entry, my_sub_assignment_submissions: [] }\n\n    if has_sub_assignment_submissions?(current_user, topic)\n      checkpoint_submissions = topic.assignment&.sub_assignment_submissions&.active&.where(user_id: current_user)\n      obj[:my_sub_assignment_submissions] = checkpoint_submissions\n    end\n\n    obj\n  rescue ActiveRecord::RecordNotFound\n    raise GraphQL::ExecutionError, \"not found\"\n  rescue InsufficientPermissionsError\n    validation_error(I18n.t(\"Insufficient Permissions\"))\n  end\n\n  def build_entry(association, message, topic, is_anonymous_author)\n    message = Api::Html::Content.process_incoming(message, host: context[:request].host, port: context[:request].port)\n    entry = association.build(message:, user: current_user, discussion_topic: topic, is_anonymous_author:)\n    raise InsufficientPermissionsError unless entry.grants_right?(current_user, session, :create)\n\n    entry\n  end\n\n  def has_sub_assignment_submissions?(current_user, topic)\n    # if group discussion context is not a course, then there will be no assignment nor submissions\n    return false if topic.context.is_a?(Group) && !topic.context.context.is_a?(Course)\n\n    course_id = topic.context.is_a?(Course) ? topic.context.id : topic.context.context.id\n\n    # for graded group discussions, .assignment for the root topic and for each child topic is the same\n    # assignment\n    topic.assignment&.reload&.has_sub_assignments? && current_user.student_enrollments.where(course_id:).exists?\n  end\n\n  class InsufficientPermissionsError < StandardError; end\nend\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_discussion_entry.rb#L75-L111","documentation":"build_entry raises the custom InsufficientPermissionsError when the freshly built DiscussionEntry does not grant :create to the current user (entry.grants_right?). resolve rescues it and converts it to a GraphQL validation error with message \"Insufficient Permissions\". This is a policy failure, not a data failure: records exist but the user may not post.","triggerScenarios":"A student replying to a locked/closed-for-comments topic; posting to an anonymous or read-only discussion without :create rights; a user whose enrollment is concluded or who is not enrolled trying to reply; topic locked until a future date.","commonSituations":"UI not honoring locked_until / closed_for_comments state; group discussions where the user is not in the group; teachers impersonating or masquerading with reduced rights; account-level discussions with restricted posting roles.","solutions":["Check the topic's locked/available state (locked_for_user, available_from/until) before rendering the reply editor.","Verify the user's enrollment and that the topic grants :create via the discussionTopic.permissions GraphQL field.","Unlock the topic or adjust the user's role/permissions if posting should be allowed.","Hide the reply UI when entryPermissions.create is false so users never hit the error."],"exampleFix":"// before: always show reply box\n<ReplyForm topicId={topic.id} />\n\n// after\n{topic.permissions.create && <ReplyForm topicId={topic.id} />}","handlingStrategy":"validation","validationCode":"const canCreate = topic.permissions?.create\nif (!canCreate) { disableReplyEditor(); return }\nif (topic.lockedForUser || isPast(topic.lockDate)) { showLockedNotice(); return }","typeGuard":"const canPost = (topic) => Boolean(topic?.permissions?.create)","tryCatchPattern":null,"preventionTips":["Gate all posting UI on entry/discussion permissions fields.","Respect locked_until and closed_for_comments state in the client.","Re-check permissions after enrollment or masquerade changes."],"tags":["graphql","permissions","authorization","discussions"],"backgroundTag":"permission-denied","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"}