{"record":{"id":"390c21588d5c35ca","repo":"instructure/canvas-lms","slug":"insufficient-permission","errorCode":null,"errorMessage":"insufficient permission","messagePattern":"insufficient permission","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/assignment_base.rb","lineNumber":308,"sourceCode":"\n    # now remove all _tags_ that are not required\n    (current_module_ids - required_module_ids).to_set.each do |module_id_to_remove|\n      # assignments can be part of multiple modules, so we have to search through all the tags\n      # and if context_module_id is the module to remove, then we need to delete the tag\n      content_tags.each do |tag|\n        if tag.context_module_id == module_id_to_remove\n          tag.destroy\n        end\n      end\n    end\n\n    # we need to reload the assignment so things get returned correctly\n    @working_assignment.reload\n  end\n\n  def ensure_destroyed\n    # check for permissions no matter what\n    raise GraphQL::ExecutionError, \"insufficient permission\" unless @working_assignment.grants_right? current_user, :delete\n\n    # if we are already destroyed, then dont do anything\n    return if @working_assignment.workflow_state == \"deleted\"\n\n    # actually destroy now.\n    SubmissionLifecycleManager.with_executing_user(@current_user) do\n      @working_assignment.destroy\n    end\n  end\n\n  def ensure_restored\n    # if we are already not destroyed, then dont do anything\n    return if @working_assignment.workflow_state != \"deleted\"\n    raise GraphQL::ExecutionError, \"insufficient permission\" unless @working_assignment.grants_right? current_user, :delete\n\n    @working_assignment.restore\n  end\n","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/assignment_base.rb#L290-L326","documentation":"ensure_destroyed checks `@working_assignment.grants_right?(current_user, :delete)` unconditionally before destroying an assignment (deleteAssignment mutation). If the caller lacks :delete on the assignment, it raises 'insufficient permission' even if the assignment is already deleted.","triggerScenarios":"Calling deleteAssignment as a user without delete rights on the assignment's course (e.g., teacher where course settings require manage rights, or a student); current_user nil due to unauthenticated token; permission check fails because the assignment belongs to a different course than assumed.","commonSituations":"Custom roles with delete withheld; API token scoped to a user with read-only access; sub-account admin without course-level delete; automation tokens whose user lost permissions after role changes.","solutions":["Grant the user :delete right (e.g., teacher/enrollment with manage assignments permission) before calling deleteAssignment.","Verify with `assignment.grants_right?(user, :delete)` in console before invoking.","Check you're authenticated as the intended user (token owner).","If testing, enroll a teacher or stub grants_right? accordingly."],"exampleFix":"// before\nmutation { deleteAssignment(input: { id }) } # as read-only user\n\n// after: ensure caller can delete\nuser.enroll(course, 'TeacherEnrollment').accept!  # or verify\nassignment.grants_right?(user, :delete) # true before calling mutation","handlingStrategy":"validation","validationCode":"if (!assignment.permissions.delete) throw new Error('cannot delete assignment')\n","typeGuard":null,"tryCatchPattern":"try {\n  await deleteAssignment({ id })\n} catch (e) {\n  if (e.message === 'insufficient permission') showNoPermissionUi()\n}","preventionTips":["Hide delete actions when permissions.delete is false","Check enrollment/role before API calls","Re-check permissions after role changes"],"tags":["graphql","permissions","delete","canvas-lms"],"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"}