{"record":{"id":"d1af026c8fd801ed","repo":"instructure/canvas-lms","slug":"quizzes-quizsubmission-update-scores-called-on-a-quiz-that","errorCode":null,"errorMessage":"Quizzes::QuizSubmission.update_scores called on a quiz that appears to be in progress","messagePattern":"Quizzes::QuizSubmission\\.update_scores called on a quiz that appears to be in progress","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"app/models/quizzes/quiz_submission.rb","lineNumber":769,"sourceCode":"    submission&.mark_unread(user)\n    versions = self.versions\n    version = versions.current\n    version = versions.get(params[:submission_version_number]) if params[:submission_version_number]\n    # NOTE: that self may not match versions.current, because we only save a new version on actual submit\n    raise \"Can't update submission scores unless it's completed\" if !completed? && !params[:submission_version_number]\n\n    data = version.model.submission_data || []\n    res = []\n    tally = 0\n    completed_before_changes = completed?\n    self.workflow_state = \"complete\"\n    self.fudge_points = params[:fudge_points].to_f if params[:fudge_points] && params[:fudge_points] != \"\"\n    tally += fudge_points if fudge_points\n    data.each do |answer|\n      unless answer.respond_to?(:with_indifferent_access)\n        logger.error \"submission = #{to_json}\"\n        logger.error \"answer = #{answer.inspect}\"\n        raise \"Quizzes::QuizSubmission.update_scores called on a quiz that appears to be in progress\"\n      end\n      answer = answer.with_indifferent_access\n      score = params[:\"question_score_#{answer[\"question_id\"]}\"]\n      answer[\"more_comments\"] = params[:\"question_comment_#{answer[\"question_id\"]}\"] if params[:\"question_comment_#{answer[\"question_id\"]}\"]\n      if score.present?\n        begin\n          float_score = score.to_f\n        rescue\n          float_score = nil\n        end\n        answer[\"points\"] = float_score || answer[\"points\"] || 0\n        answer[\"correct\"] = \"defined\" if answer[\"correct\"] == \"undefined\" && float_score\n      elsif score && score.empty?\n        answer[\"points\"] = 0\n        answer[\"correct\"] = \"undefined\"\n      end\n      if answer[\"correct\"] == \"undefined\"\n        question = quiz_data.find { |h| h[:id] == answer[\"question_id\"] }","sourceCodeStart":751,"sourceCodeEnd":787,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/quizzes/quiz_submission.rb#L751-L787","documentation":"Inside update_scores, each entry of the version's submission_data is expected to be a hash (with_indifferent_access). If an entry is a scalar/string, the submission data is considered corrupt or from an in-progress attempt, so Canvas logs the submission JSON and answer, then raises this RuntimeError. It is an internal data-integrity guard.","triggerScenarios":"submission_data for the selected version contains non-hash entries — typically an attempt still in progress was saved as raw/scalar data, or the stored version data is corrupted/legacy-shaped.","commonSituations":"Scoring a version whose model.submission_data was serialized mid-attempt; database rows written by older Canvas versions or interrupted saves; data migrations leaving malformed submission_data; race where the version snapshot was captured before answers became hashes.","solutions":["Inspect submission_data for the targeted version and repair/normalize non-hash entries to hashes","Re-grade against a completed version (pass submission_version_number) whose data is well-formed","Re-save/re-grade the submission (grade_the_submission) to regenerate properly structured data","Check logs (submission to_json and answer inspect are logged at error) to identify the corrupted record and fix the data"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"bad = Array(version.model.submission_data).reject { |a| a.respond_to?(:with_indifferent_access) }\nraise CorruptSubmissionData if bad.any?","typeGuard":"def valid_submission_data?(data) = data.is_a?(Array) && data.all? { |a| a.is_a?(Hash) || a.respond_to?(:with_indifferent_access) }","tryCatchPattern":"begin\n  submission.update_scores(user, params)\nrescue RuntimeError => e\n  raise unless e.message.include?(\"appears to be in progress\")\n  flag_submission_for_repair(submission)\nend","preventionTips":["Validate submission_data shape before grading","Keep version snapshots taken only after attempts complete","Monitor error logs for corrupted submissions and repair data"],"tags":["ruby","rails","quiz-submission","data-integrity"],"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"}