instructure/canvas-lms · error · InvalidDataError
Points for tier must be less than points for prior tier (…
Error message
Points for tier %{index} must be less than points for prior tier (%{points} is greater than %{prior}) What it means
Raised by Outcomes::CsvImporter#parse_ratings when a tier's points value is greater than the previous tier's points. Outcome ratings must be in descending point order (first tier is highest); each parsed points value must be <= the prior one or an InvalidDataError reports both values. This preserves Canvas's expectation that mastery ratings go from highest to lowest.
Solutions
- Reverse the rating order so points descend: highest tier (e.g. Exemplary) first.
- Re-sort the ratings pairs by points descending before importing.
- Verify each tier's points is less than or equal to the previous tier's points.
Example fix
// before ratings: 1,Beginning,3,Meets,5,Exemplary // after ratings: 5,Exemplary,3,Meets,1,Beginning
Defensive patterns
Strategy: validation
Validate before calling
points = cells.each_slice(2).map(&:first).compact.map(&:to_f)
raise 'rating points must descend' unless points.each_cons(2).all? { |a, b| a >= b } Try / catch
begin
importer.run
rescue Outcomes::Import::InvalidDataError => e
errors << "Row #{i}: #{e.message}"
end Prevention
- List rating tiers highest-points first (Exemplary before Meets)
- Sort ratings pairs by points descending in preprocessing
- Visual check: points column should read top-to-bottom as non-increasing
When it happens
Trigger: Ratings written in ascending order, e.g. `3,Meets,5,Exemplary` — at tier 2, points 5 > prior 3; duplicated or shuffled point values; users listing tiers from lowest to highest mastery.
Common situations: Authors thinking of ratings as ascending rubric levels; reordering tiers in a spreadsheet without re-sorting points; converting from tools that use ascending scales.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/b40a5d7b194a1f72.
Report an issue: GitHub.
Appendix: source
Thrown at lib/outcomes/csv_importer.rb:179
if object[:mastery_points].present?
object[:mastery_points] = strict_parse_float(object[:mastery_points], I18n.t("mastery points"))
end
if object[:friendly_description].present? && object[:friendly_description].length > 255
raise InvalidDataError, I18n.t("Friendly description is too long (maximum is 255 characters)")
end
import_object(object)
end
def parse_ratings(ratings)
prior = nil
drop_trailing_nils(ratings).each_slice(2).to_a.map.with_index(1) do |(points, description), index|
raise InvalidDataError, I18n.t("Points for rating tier %{index} not present", index:) if points.nil? || points.blank?
points = strict_parse_float(points, I18n.t("rating tier %{index} threshold", index:))
if prior.present? && prior < points
raise InvalidDataError, I18n.t(
"Points for tier %{index} must be less than points for prior tier (%{points} is greater than %{prior})",
index:,
prior:,
points:
)
end
prior = points
{ points:, description: }
end
end
def normalize_i18n(string)
raise ArgumentError if string.blank?
separator = I18n.t("number.format.separator")
delimiter = I18n.t("number.format.delimiter")
string.gsub(delimiter, "").gsub(separator, ".")View on GitHub (pinned to 1c9f0bb801)