instructure/canvas-lms · error · RuntimeError

Could not find valid answer

Error message

Could not find valid answer

What it means

Quizzes::QuizOutcomeResultBuilder#create_outcome_question_result raises when the student's submission_data contains no answer for the cached question (matched by question_id). The submission snapshot has the question but no recorded answer record, so an outcome result for that question cannot be built.

Solutions

  1. Inspect the quiz submission's submission_data to see which question_ids are present
  2. Confirm the student actually answered that question; skip outcome result generation for unanswered questions
  3. Guard with a conditional skip rather than raise when cached_answer is nil
  4. Re-generate submission_data (regrade/rebuild snapshot) if it is corrupted relative to quiz_data

Example fix

// before
raise "Could not find valid answer" unless cached_answer
// after
cached_answer = @qs.submission_data.detect { |q| q[:question_id] == cached_question[:id] }
next unless cached_answer
Defensive patterns

Strategy: validation

Validate before calling

next unless @qs.quiz_data&.any? { |q| q[:assessment_question_id] == question.id } &&
  @qs.submission_data.to_a.any? { |a| a[:question_id].to_s == cached_question[:id].to_s }

Type guard

has_answer = ->(qs, qid) { qs.submission_data.to_a.any? { |a| a[:question_id].to_s == qid.to_s } }

Try / catch

begin
  builder.build_outcome_results(alignment, questions)
rescue RuntimeError => e
  Rails.logger.warn("skipping question without answer: #{e.message}")
end

Prevention

When it happens

Trigger: @qs.submission_data lacks an entry whose :question_id equals cached_question[:id] — e.g. the question was added to quiz_data after submission, answer data was pruned, or an unanswered/skipped question produced no submission_data entry.

Common situations: Student never answered the question and no placeholder answer record exists; quiz_data and submission_data versions are out of sync; external/essay questions migrated leaving inconsistent snapshots; data cleanup jobs removed submission_data entries.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/8dc7df6d41556baa. Report an issue: GitHub.

Appendix: source

Thrown at app/models/quizzes/quiz_outcome_result_builder.rb:58

    def create_outcome_question_result(question, alignment)
      # find or create the user's unique LearningOutcomeResult for this alignment
      # of the quiz question.
      quiz_result = alignment.learning_outcome_results
                             .for_association(@qs.quiz)
                             .for_associated_asset(@qs.quiz)
                             .where(user_id: @qs.user.id)
                             .first_or_initialize

      quiz_result.workflow_state = :active
      quiz_result.user_uuid = @qs.user.uuid

      # get data from quiz submission's question result to ensure result should be generated
      cached_question = @qs.quiz_data.detect { |q| q[:assessment_question_id] == question.id }
      raise "Could not find valid question" unless cached_question

      cached_answer = @qs.submission_data.detect { |q| q[:question_id] == cached_question[:id] }
      raise "Could not find valid answer" unless cached_answer

      # Create a question scoped outcome result linked to the quiz_result.
      question_result = quiz_result.learning_outcome_question_results.for_associated_asset(question).first_or_initialize

      # do not create a result if no points are possible.
      if cached_question["points_possible"] == 0
        # destroy any existing results that might be persisted if points possible were not always 0
        question_result.destroy if question_result.persisted?
        return
      end

      # update the result with stuff from the quiz submission's question result
      question_result.learning_outcome = quiz_result.learning_outcome

      # mastery
      question_result.score = cached_answer[:points]
      question_result.possible = cached_question["points_possible"]
      question_result.calculate_percent!

View on GitHub (pinned to 1c9f0bb801)