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
- Verify the AssessmentQuestion id exists in the quiz submission's quiz_data for that student's submission
- Check whether the quiz was edited/question regenerated after the submission; align outcomes against the question version the student actually saw
- Skip gracefully instead of raising when the question is not in quiz_data (wrap in a find and next)
- 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
- Lock quizzes (or freeze question versions) after submissions exist
- When regenerating assessment questions, map old ids to new ids for outcome alignment
- Process outcome results per submission version matching the alignment's version
- Prefer skipping unmatched questions over raising in batch result generation
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
- Could not find valid answer
- Cannot archive a deleted LearningOutcome
- Cannot archive a deleted LearningOutcomeGroup
- Cannot modify outcome from another context
- Cannot set rubric self assessment for quiz assignments
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
# masteryView on GitHub (pinned to 1c9f0bb801)