instructure/canvas-lms · error · RuntimeError
Quizzes::QuizSubmission.update_scores called on a quiz that…
Error message
Quizzes::QuizSubmission.update_scores called on a quiz that appears to be in progress
What it means
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.
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
Defensive patterns
Strategy: try-catch
Validate before calling
bad = Array(version.model.submission_data).reject { |a| a.respond_to?(:with_indifferent_access) }
raise CorruptSubmissionData if bad.any? Type guard
def valid_submission_data?(data) = data.is_a?(Array) && data.all? { |a| a.is_a?(Hash) || a.respond_to?(:with_indifferent_access) } Try / catch
begin
submission.update_scores(user, params)
rescue RuntimeError => e
raise unless e.message.include?("appears to be in progress")
flag_submission_for_repair(submission)
end Prevention
- Validate submission_data shape before grading
- Keep version snapshots taken only after attempts complete
- Monitor error logs for corrupted submissions and repair data
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Can't update submission scores unless it's completed
- Only a hash value is accepted for backup_submission_data…
- assessor and assessee required
- association required
- can only forward one conversation at a time
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/d1af026c8fd801ed.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/quizzes/quiz_submission.rb:769
submission&.mark_unread(user)
versions = self.versions
version = versions.current
version = versions.get(params[:submission_version_number]) if params[:submission_version_number]
# NOTE: that self may not match versions.current, because we only save a new version on actual submit
raise "Can't update submission scores unless it's completed" if !completed? && !params[:submission_version_number]
data = version.model.submission_data || []
res = []
tally = 0
completed_before_changes = completed?
self.workflow_state = "complete"
self.fudge_points = params[:fudge_points].to_f if params[:fudge_points] && params[:fudge_points] != ""
tally += fudge_points if fudge_points
data.each do |answer|
unless answer.respond_to?(:with_indifferent_access)
logger.error "submission = #{to_json}"
logger.error "answer = #{answer.inspect}"
raise "Quizzes::QuizSubmission.update_scores called on a quiz that appears to be in progress"
end
answer = answer.with_indifferent_access
score = params[:"question_score_#{answer["question_id"]}"]
answer["more_comments"] = params[:"question_comment_#{answer["question_id"]}"] if params[:"question_comment_#{answer["question_id"]}"]
if score.present?
begin
float_score = score.to_f
rescue
float_score = nil
end
answer["points"] = float_score || answer["points"] || 0
answer["correct"] = "defined" if answer["correct"] == "undefined" && float_score
elsif score && score.empty?
answer["points"] = 0
answer["correct"] = "undefined"
end
if answer["correct"] == "undefined"
question = quiz_data.find { |h| h[:id] == answer["question_id"] }View on GitHub (pinned to 1c9f0bb801)