instructure/canvas-lms · error · DataFormatError

Invalid CSV File

Error message

Invalid CSV File

What it means

Outcomes::CsvImporter#run parses an outcomes CSV; if CSV parsing raises CSV::MalformedCSVError (structurally broken CSV — unbalanced quotes, bad encoding artifacts, invalid row shapes), the importer wraps it as DataFormatError with the i18n message 'Invalid CSV File'. This signals the file itself is not parseable CSV rather than a data/content problem.

Solutions

  1. Open the file in a CSV validator or spreadsheet app and fix malformed quoting/rows, then re-import.
  2. Re-export the CSV as UTF-8 with standard double-quote escaping from the source system.
  3. Check for truncated uploads (compare file size/line count to the source) and re-upload.
  4. If generating the CSV programmatically, write it with a proper CSV library instead of string concatenation.

Example fix

// before (manual CSV generation)
out << "#{name},#{url}" # breaks when name contains commas/quotes
// after
require 'csv'
out << CSV.generate_line([name, url])
Defensive patterns

Strategy: validation

Validate before calling

begin
  CSV.parse(File.read(path))
rescue CSV::MalformedCSVError => e
  abort "invalid CSV: #{e.message}"
end

Type guard

def parseable_csv?(path)
  CSV.parse(File.read(path))
  true
rescue CSV::MalformedCSVError, ArgumentError
  false
end

Try / catch

begin
  importer.run
rescue Outcomes::CsvImporter::DataFormatError => e
  flash[:error] = e.message # 'Invalid CSV File'
end

Prevention

When it happens

Trigger: Uploading/importing an outcomes CSV where CSV.parse-level parsing fails: unterminated quotes, stray characters after a quoted field, mixed encodings producing invalid bytes.

Common situations: Files edited in Excel and re-saved with odd quoting; files exported with a non-UTF8 encoding; truncated uploads; CSVs using multiline quoted fields that got mangled by copy/paste.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/2ab542198e541dfd. Report an issue: GitHub.

Appendix: source

Thrown at lib/outcomes/csv_importer.rb:63

    BATCH_SIZE = 1000

    def initialize(import, file)
      @import = import
      @file = file
    end

    delegate :context, to: :@import

    def run(&)
      status = { progress: 0, errors: [] }
      yield status

      file_errors = []
      begin
        parse_file(&)
      rescue CSV::MalformedCSVError
        raise DataFormatError, I18n.t("Invalid CSV File")
      rescue ParseError => e
        raise DataFormatError, e.message
      rescue ActiveRecord::StatementInvalid => e
        raise DataFormatError, I18n.t("Database error (%{err})", err: e.message)
      end
      status = {
        errors: file_errors,
        progress: 100
      }
      yield status
    end

    def parse_file
      headers = nil
      total = file_line_count
      raise ParseError, I18n.t("File has no data") if total < 1

      separator = test_header_i18n

View on GitHub (pinned to 1c9f0bb801)