instructure/canvas-lms · error · ParseError
Invalid fields after ratings
Error message
Invalid fields after ratings: %{fields} What it means
Raised by Outcomes::CsvImporter#validate_headers when the header row contains non-empty, non-nil values in the ratings* columns that are not the literal 'ratings' column position terminator — i.e. content appears immediately after the 'ratings' header that the importer cannot interpret. The importer treats everything from 'ratings' onward as rating pairs and requires nothing extra after the ratings block.
Solutions
- Remove any column headers/cells after the 'ratings' column; only rating points/description pairs belong there.
- Regenerate the CSV from the official Canvas outcome import template.
- Verify the header row exactly: required fields first, then a single 'ratings' column containing point/description pairs.
Example fix
// before vendor_guid,title,ratings,notes // after vendor_guid,title,ratings
Defensive patterns
Strategy: validation
Validate before calling
headers = CSV.open(path, &:first)
idx = headers.index('ratings')
extra = headers[(idx + 1)..].to_a.compact_blank
raise "unexpected fields after ratings: #{extra}" unless extra.empty? Try / catch
begin
importer.parse_file
rescue Outcomes::CsvImporter::ParseError => e
notify_user("Header row problem: #{e.message}")
end Prevention
- Never add columns after the ratings column
- Use the official template as the single source of truth
- Diff your header row against the template before each import
When it happens
Trigger: A CSV header like `vendor_guid,title,description,ratings,extra_column` where 'extra_column' follows 'ratings' and its pairs; stray trailing values after the last rating description; commas at the end of the header row creating empty-but-shifted cells the importer interprets as extra fields.
Common situations: Hand-edited CSVs adding a custom column after the ratings section; exporting from another tool that appends columns; template misuse where rating pairs are misaligned so a description lands in the wrong slot.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid fields
- Friendly description is too long (maximum is 255 characters)
- Invalid value for
- Missing required fields
- Missing 'Rubric Name' in some rows.
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/bac4e06a80ed27ac.
Report an issue: GitHub.
Appendix: source
Thrown at lib/outcomes/csv_importer.rb:143
@file.rewind
count
end
def check_encoding(str)
encoded = str&.force_encoding("utf-8")
valid = (encoded || "").valid_encoding?
raise ParseError, I18n.t("Not a valid utf-8 string: %{string}", string: str.inspect) unless valid
encoded
end
def validate_headers(row, _index)
main_columns_end = row.find_index("ratings") || row.length
headers = row.slice(0, main_columns_end).map(&:to_sym)
after_ratings = row[(main_columns_end + 1)..] || []
after_ratings = after_ratings.compact_blank.map(&:to_s)
raise ParseError, I18n.t("Invalid fields after ratings: %{fields}", fields: after_ratings.inspect) unless after_ratings.empty?
missing = (REQUIRED_FIELDS - headers).map(&:to_s)
raise ParseError, I18n.t("Missing required fields: %{fields}", fields: missing.inspect) unless missing.empty?
invalid = (headers - OPTIONAL_FIELDS - REQUIRED_FIELDS).map(&:to_s)
raise ParseError, I18n.t("Invalid fields: %{fields}", fields: invalid.inspect) unless invalid.empty?
headers
end
def import_row(headers, row)
simple = headers.zip(row).to_h
ratings = row[headers.length..]
object = simple.to_h
object[:ratings] = parse_ratings(ratings)
object[:learning_outcome_group_id] = @import[:learning_outcome_group_id]
if object[:mastery_points].present?View on GitHub (pinned to 1c9f0bb801)