{"record":{"id":"95ac2b631bc61cc9","repo":"instructure/canvas-lms","slug":"missing-ratings-for-criterion-description","errorCode":null,"errorMessage":"Missing ratings for #{criterion[:description]}","messagePattern":"Missing ratings for #(.+?)","errorType":"validation","errorClass":"DataFormatError","httpStatus":null,"severity":"error","filePath":"app/models/rubric_import.rb","lineNumber":116,"sourceCode":"      job_failed!\n    end\n  end\n\n  def process_rubrics\n    rubrics_by_name = RubricCSVImporter.new(attachment).parse\n    raise DataFormatError, I18n.t(\"The file is empty or does not contain valid rubric data.\") if rubrics_by_name.empty?\n\n    total_rubrics = rubrics_by_name.keys.count\n    error_data = []\n\n    rubrics_by_name.each_with_index do |(rubric_name, rubric_data), rubric_index|\n      raise DataFormatError, I18n.t(\"Missing 'Rubric Name' in some rows.\") if rubric_name.blank?\n\n      rubric = context.rubrics.build(rubric_imports_id: id)\n      criteria_hash = {}\n      rubric_data.each_with_index do |criterion, criterion_index|\n        raise DataFormatError, \"Missing 'Criteria Name' for #{rubric_name}\" if criterion[:description].blank?\n        raise DataFormatError, \"Missing ratings for #{criterion[:description]}\" if criterion[:ratings].empty?\n\n        ratings_hash = {}\n        criterion[:ratings].each_with_index do |rating, rating_index|\n          ratings_hash[rating_index.to_s] = {\n            \"description\" => rating[:description],\n            \"long_description\" => rating[:long_description],\n            \"points\" => rating[:points]\n          }\n        end\n        criteria_hash[criterion_index.to_s] = {\n          \"description\" => criterion[:description],\n          \"long_description\" => criterion[:long_description],\n          \"ratings\" => ratings_hash\n        }\n        if context.root_account.feature_enabled?(:rubric_criterion_range)\n          criteria_hash[criterion_index.to_s][\"criterion_use_range\"] = criterion[:criterion_use_range]\n        end\n      end","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/rubric_import.rb#L98-L134","documentation":"RubricImport#process_rubrics requires every criterion to have at least one rating; a criterion with criterion[:ratings] empty cannot form a valid rubric criterion, so a DataFormatError is raised referencing the criterion description. Canvas rubrics model all scoring through ratings, so an empty ratings array is invalid.","triggerScenarios":"Call process_rubrics where a criterion row has a description but its parsed :ratings array is empty or missing (no rating rows under that criterion in the import data).","commonSituations":"CSV import where a criterion row exists but its rating rows were deleted or misaligned (e.g. rating columns moved to another criterion); hand-built import hashes forgetting the ratings key; spreadsheet filters hiding rating rows on export.","solutions":["Add at least one rating (description + points) under the named criterion in the import file and re-import.","Fix the parsing step so rating rows are correctly attached to their parent criterion instead of landing in another criterion's array.","Pre-validate rubric_data in the caller and reject criteria with empty ratings before process_rubrics runs."],"exampleFix":"// before\ncriterion = { description: 'Quality', ratings: [] }\nimport.process_rubrics # => DataFormatError: Missing ratings for Quality\n\n// after\ncriterion = { description: 'Quality', ratings: [{ description: 'Excellent', points: 10 }, { description: 'Poor', points: 0 }] }\nimport.process_rubrics # succeeds","handlingStrategy":"validation","validationCode":"missing = rubric_data.reject { |c| c[:ratings].is_a?(Array) && c[:ratings].any? }\nraise ArgumentError, \"criteria without ratings: #{missing.map { |c| c[:description] }.join(', ')}\" if missing.any?","typeGuard":"def has_ratings?(criterion)\n  criterion[:ratings].is_a?(Array) && !criterion[:ratings].empty?\nend","tryCatchPattern":"begin\n  import.process_rubrics\nrescue DataFormatError => e\n  flash[:error] = e.message\n  redirect_to rubric_import_path(import)\nend","preventionTips":["Ensure each criterion in the import file has at least one rating row beneath it.","Verify CSV parsing attaches rating rows to the correct parent criterion.","Run a dry-run parse of the file and dump criteria/ratings counts before importing."],"tags":["rubric-import","validation","ratings","csv-import"],"backgroundTag":"empty-required-field","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}