instructure/canvas-lms · error · ParseError

File has no outcomes data

Error message

File has no outcomes data

What it means

In Outcomes::CsvImporter#parse_file, after the first batch of rows is consumed for header validation, the remaining batch is checked; if it is empty the file contained a header but no outcome rows, so ParseError 'File has no outcomes data' is raised. This distinguishes a header-only CSV from the completely empty case.

Solutions

  1. Add at least one outcome data row beneath the header and re-import.
  2. Verify the header row matches the expected outcomes import format so data rows are not misparsed.
  3. Check line endings/encoding (e.g. BOM or CR-only files) that could prevent data rows from being read.
  4. Warn users in the UI when the selected file has only one line before submitting.

Example fix

// before (header only)
"title,vendor_guid,display_name\n"
// after
"title,vendor_guid,display_name\nMy Outcome,vo-1,My Outcome\n"
Defensive patterns

Strategy: validation

Validate before calling

rows = CSV.read(path)
raise 'header-only CSV' if rows.size < 2

Type guard

def has_data_rows?(path)
  rows = CSV.read(path)
  rows.is_a?(Array) && rows.size > 1
end

Try / catch

begin
  importer.run
rescue Outcomes::CsvImporter::ParseError => e
  flash[:error] = e.message # 'File has no outcomes data'
end

Prevention

When it happens

Trigger: Importing an outcomes CSV that contains only the header row (or fewer than BATCH_SIZE rows where the only row was consumed as the header), so the first batch.shift empties the batch.

Common situations: Users upload the untouched CSV template with headers but never filled in data rows; a filter/export step dropped all data rows; row endings/encoding caused all data rows to be skipped leaving only the header.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at lib/outcomes/csv_importer.rb:85

        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
      rows = CSV.new(@file, col_sep: separator).to_enum
      rows.with_index(1).each_slice(BATCH_SIZE) do |batch|
        headers ||= validate_headers(*batch.shift)
        raise ParseError, I18n.t("File has no outcomes data") if batch.empty?

        errors = parse_batch(headers, batch)
        status = {
          errors:,
          progress: (batch.last[1].to_f / total * 100).floor
        }
        yield status
      end
    end

    def parse_batch(headers, batch)
      Account.transaction do
        results = batch.map do |row, line|
          utf8_row = row.map(&method(:check_encoding))
          import_row(headers, utf8_row) unless utf8_row.all?(&:blank?)
          []
        rescue ParseError, InvalidDataError => e
          [[line, e.message]]

View on GitHub (pinned to 1c9f0bb801)