instructure/canvas-lms · error · RuntimeError
Can't update submission scores unless it's completed
Error message
Can't update submission scores unless it's completed
What it means
QuizSubmission#update_scores applies teacher-entered scores to the current submission version, but only meaningful on a completed submission. Canvas raises this RuntimeError unless completed? or an explicit submission_version_number param targets a historical version. It prevents regrading quizzes still in progress.
Solutions
- Wait for or force completion of the submission before updating scores
- Pass params[:submission_version_number] pointing to a completed historical version to score that version instead
- Check quiz_submission.completed? before calling update_scores and skip or queue the update
- Ensure the user cannot be actively taking the quiz when grading (lock the attempt)
Example fix
// before submission.update_scores(current_user, params) // after if submission.completed? || params[:submission_version_number] submission.update_scores(current_user, params) else raise QuizSubmissionNotCompleted end
Defensive patterns
Strategy: validation
Validate before calling
raise ScoresUpdateNotAllowed unless submission.completed? || params[:submission_version_number].present?
Try / catch
begin
submission.update_scores(user, params)
rescue RuntimeError => e
raise unless e.message.include?("unless it's completed")
queue_regrade_when_completed(submission, params)
end Prevention
- Check completed? before score updates
- Pass submission_version_number for historical versions
- Lock in-progress attempts before manual grading
When it happens
Trigger: Calling update_scores (e.g. from controller regrade actions) for a submission whose workflow_state is not complete, without passing submission_version_number.
Common situations: Teacher grading UI open across a student re-attempt; scripts regrading submissions mid-attempt; a student retaking a quiz while scores are being posted; moderator/extension flows resetting attempts.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Only a hash value is accepted for backup_submission_data…
- Quizzes::QuizSubmission.update_scores called on a quiz that…
- 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/122a8fc126b9e8f4.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/quizzes/quiz_submission.rb:756
if finished_at
quiz.context_module_action(user, :submitted, kept_score) # pass in the score so we don't accidentally unset a min_score requirement
end
end
end
end
def update_scores(params)
original_score = score
original_workflow_state = workflow_state
params = (params || {}).with_indifferent_access
self.manually_scored = false
self.grader_id = params[:grader_id]
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?View on GitHub (pinned to 1c9f0bb801)