{"record":{"id":"845c58acf698014f","repo":"instructure/canvas-lms","slug":"user-not-authorized-to-give-final-provisional-grades","errorCode":null,"errorMessage":"User not authorized to give final provisional grades","messagePattern":"User not authorized to give final provisional grades","errorType":"exception","errorClass":"Assignment::GradeError","httpStatus":null,"severity":"error","filePath":"app/models/submission.rb","lineNumber":2550,"sourceCode":"             pgs.detect { |pg2| !pg2.final && pg2.scorer_id == scorer.id }\n           end\n         elsif final\n           provisional_grades.final.first\n         else\n           provisional_grades.not_final.where(scorer_id: scorer).first\n         end\n\n    if default_to_null_grade && pg.nil?\n      ModeratedGrading::NullProvisionalGrade.new(self, scorer.id, final)\n    else\n      pg\n    end\n  end\n\n  def find_or_create_provisional_grade!(scorer, attrs = {})\n    ModeratedGrading::ProvisionalGrade.unique_constraint_retry do\n      if attrs[:final] && !assignment.permits_moderation?(scorer)\n        raise Assignment::GradeError, \"User not authorized to give final provisional grades\"\n      end\n\n      pg = find_existing_provisional_grade(scorer, attrs[:final]) || provisional_grades.build\n      pg = update_provisional_grade(pg, scorer, attrs)\n      pg.save! if attrs[:force_save] || pg.new_record? || pg.changed?\n      pg\n    end\n  end\n\n  def find_existing_provisional_grade(scorer, final)\n    final ? provisional_grades.final.first : provisional_grades.not_final.find_by(scorer:)\n  end\n\n  def moderated_grading_allow_list(current_user = user, loaded_attachments: nil)\n    return nil unless assignment.moderated_grading? && current_user.present?\n\n    moderation_allow_list_for_user(current_user).map(&:moderated_grading_ids)\n  end","sourceCodeStart":2532,"sourceCodeEnd":2568,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/submission.rb#L2532-L2568","documentation":"Submission#find_or_create_provisional_grade! creates or updates a ModeratedGrading::ProvisionalGrade for a scorer. When attrs[:final] is true, the scorer must hold moderation permission on the assignment (assignment.permits_moderation?); otherwise Assignment::GradeError 'User not authorized to give final provisional grades' is raised. Final provisional grades are reserved for the assignment's moderator(s).","triggerScenarios":"Call submission.find_or_create_provisional_grade!(scorer, { final: true, ... }) where scorer is a TA/provisional grader without moderation rights (not the final grader / moderator, no 'Moderate Grades' permission on the course/assignment).","commonSituations":"API or UI request from a provisional grader (non-moderator TA) attempting to post a final grade during moderated grading; custom integrations passing final: true for any scorer; permission changes removing the moderator role after grades were being posted.","solutions":["Ensure the scorer has the moderator role / 'Select final grade' permission on the assignment before passing final: true.","Call with final: false for provisional (non-final) grades from non-moderator graders.","Check assignment.permits_moderation?(scorer) client-side or in the controller and return a 401/403 before invoking the method.","If the user should be a moderator, grant them the appropriate role (e.g. add as moderator on the moderated assignment / enroll with grading permissions)."],"exampleFix":"// before\nsubmission.find_or_create_provisional_grade!(scorer, { final: true, score: 92 })\n# => Assignment::GradeError for non-moderator\n\n// after\nif assignment.permits_moderation?(scorer)\n  submission.find_or_create_provisional_grade!(scorer, { final: true, score: 92 })\nelse\n  submission.find_or_create_provisional_grade!(scorer, { final: false, score: 92 })\nend","handlingStrategy":"validation","validationCode":"unless assignment.permits_moderation?(scorer)\n  return render json: { error: 'not authorized for final provisional grades' }, status: :forbidden\nend","typeGuard":"def can_give_final_provisional_grade?(assignment, user)\n  assignment.permits_moderation?(user)\nend","tryCatchPattern":"begin\n  submission.find_or_create_provisional_grade!(scorer, final: true, score: score)\nrescue Assignment::GradeError => e\n  render json: { error: e.message }, status: :unauthorized\nend","preventionTips":["Only pass final: true for users with moderation rights on the assignment.","Check assignment.permits_moderation?(scorer) before calling provisional-grade APIs.","Provisional graders should always use final: false."],"tags":["moderated-grading","authorization","provisional-grade","permissions"],"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"}