instructure/canvas-lms · error · RuntimeError

Could not find valid question

Error message

Could not find valid question

What it means

Quizzes::QuizOutcomeResultBuilder#create_outcome_question_result looks up the question in the quiz submission's cached quiz_data (snapshotted at submission time) by assessment_question_id and raises if it cannot be found. This means the AssessmentQuestion being aligned to outcomes was not part of the quiz when the student took it, so no outcome result can be generated for it.

Solutions

  1. Verify the AssessmentQuestion id exists in the quiz submission's quiz_data for that student's submission
  2. Check whether the quiz was edited/question regenerated after the submission; align outcomes against the question version the student actually saw
  3. Skip gracefully instead of raising when the question is not in quiz_data (wrap in a find and next)
  4. Re-score/rebuild the outcome results from a submission version whose quiz_data contains the question

Example fix

// before
raise "Could not find valid question" unless cached_question
// after
cached_question = @qs.quiz_data.detect { |q| q[:assessment_question_id] == question.id }
next unless cached_question
Defensive patterns

Strategy: validation

Validate before calling

next unless @qs.quiz_data&.any? { |q| q[:assessment_question_id] == question.id }

Type guard

find_cached = ->(qs, question) { qs.quiz_data.to_a.detect { |q| q[:assessment_question_id] == question.id } }

Try / catch

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

Prevention

When it happens

Trigger: build_outcome_results iterates aligned questions and calls create_outcome_question_result for a question whose assessment_question_id is absent from @qs.quiz_data — e.g. the question was deleted/replaced or re-generated (new assessment_question_id) after the student's submission was taken.

Common situations: Teacher edits the quiz and swaps the question bank/question after submissions exist; assessment question was migrated to a new bank changing its id; stale quiz_data from an older submission version; outcome alignment points to a question from a different quiz version.

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/3ccc3754ea010d78. Report an issue: GitHub.

Appendix: source

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

    end

    private

    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

View on GitHub (pinned to 1c9f0bb801)