{"record":{"id":"3bab551580ec95f0","repo":"instructure/canvas-lms","slug":"assignment-grade-error","errorCode":null,"errorMessage":"Assignment Grade Error","messagePattern":"Assignment Grade Error","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/save_rubric_assessment.rb","lineNumber":84,"sourceCode":"        unless association.user_can_assess_for?(assessor: current_user, assessee: user, assessment_type:)\n          raise GraphQL::ExecutionError, \"Not authorized to assess user\"\n        end\n\n        rubric_assessment = association.assess(\n          assessor: current_user,\n          user:,\n          artifact: asset,\n          assessment: assessment_details,\n          graded_anonymously: input[:graded_anonymously]\n        )\n\n        submission.reload\n        return { submission:, rubric_assessment:, rubric_association: association }\n      end\n    rescue Assignment::MaxGradersReachedError => e\n      raise GraphQL::ExecutionError, e.message\n    rescue Assignment::GradeError\n      raise GraphQL::ExecutionError, \"Assignment Grade Error\"\n    end\n  rescue ActiveRecord::RecordNotFound => e\n    raise GraphQL::ExecutionError, \"#{e.model} not found\"\n  end\n\n  def ensure_adjudication_possible(provisional:, association_object:, grader:, &)\n    # Non-assignment association objects crash if they're passed into this\n    # controller, since find_asset_for_assessment only exists on assignments.\n    # The check here thus serves only to make sure the crash doesn't happen on\n    # the call below.\n    return yield unless association_object.is_a?(Assignment)\n\n    association_object.ensure_grader_can_adjudicate(\n      grader:,\n      provisional:,\n      occupy_slot: true,\n      &\n    )","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/save_rubric_assessment.rb#L66-L102","documentation":"The inner block rescues Assignment::GradeError and converts it to a generic \"Assignment Grade Error\" GraphQL::ExecutionError. This is Canvas's internal grading failure — an error raised inside the grade/assess pipeline while persisting the grade or assessment artifact.","triggerScenarios":"association.assess or the surrounding grading flow raises Assignment::GradeError — e.g. grade conflicts, invalid grade state, or versioning failures during rubric assessment save.","commonSituations":"Grading a submission whose grade state changed concurrently (another grader saved first); inconsistent rubric_association points; versioned-submission edge cases.","solutions":["Check Canvas logs/production.stacktrace for the original Assignment::GradeError backtrace to find the real cause.","Reload the submission and retry once; if the grade was already saved by another grader, skip.","Validate rubric data (points, criterion ids) before calling the mutation."],"exampleFix":"// before\nrescue Assignment::GradeError\n  raise GraphQL::ExecutionError, 'Assignment Grade Error'\n// after\nrescue Assignment::GradeError => e\n  Rails.logger.error(\"GradeError in save_rubric_assessment: #{e.message}\")\n  raise GraphQL::ExecutionError, \"Assignment Grade Error: #{e.message}\"","handlingStrategy":"try-catch","validationCode":"// validate rubric payload before assess\nconst totalPoints = criteria.reduce((sum, c) => sum + (c.points ?? 0), 0)\nif (!criteria.length || Number.isNaN(totalPoints)) throw new Error('invalid rubric data')","typeGuard":null,"tryCatchPattern":"try { await saveRubricAssessment(...) } catch (e) { if (e.message === 'Assignment Grade Error') { logFullError(e); showError('Grading failed; reload and retry') } }","preventionTips":["Serialize grade saves per submission to avoid conflicts","Keep rubric criterion ids/points in sync with the association","Monitor server logs for the underlying Assignment::GradeError cause"],"tags":["graphql","grading","rubric","error-handling"],"backgroundTag":"internal-invariant-violation","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"}