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

  1. Wait for or force completion of the submission before updating scores
  2. Pass params[:submission_version_number] pointing to a completed historical version to score that version instead
  3. Check quiz_submission.completed? before calling update_scores and skip or queue the update
  4. 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

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


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)