instructure/canvas-lms · error

Empty file

Error message

Empty file

What it means

During SIS CSV parallel import, try_importing_segment opens the parallel importer's attachment and stats the file before processing. A zero-byte file means there is no CSV data to process, so the segment is rejected with 'Empty file' before importer_object.process runs.

Solutions

  1. Check which CSV attachment is empty and regenerate/re-upload it with actual data
  2. Fix the upstream export to skip (not emit) data types with no rows
  3. Add a pre-flight size check on the SIS zip contents before submitting the batch
  4. Ensure the upload completed (compare byte sizes/checksums against source) before enqueueing

Example fix

// before
SisBatch.submit_with_files(files: [empty_csv])
// after
non_empty = files.select { |f| File.size(f) > 0 }
raise 'no CSV data' if non_empty.empty?
SisBatch.submit_with_files(files: non_empty)
Defensive patterns

Strategy: validation

Validate before calling

raise 'empty SIS csv' unless parallel_importer.attachment && parallel_importer.attachment.size > 0

Try / catch

begin
  batch.run_parallel_importers
rescue RuntimeError => e
  raise unless e.message == 'Empty file'
  batch.mark_errored('a SIS CSV segment was empty')
end

Prevention

When it happens

Trigger: An SIS batch was submitted where one of the split parallel-import CSV attachments is 0 bytes — e.g. the upload was truncated, an empty file was attached for a data type, or the SIS integration generated a header-less/empty export for that segment.

Common situations: Upstream SIS export jobs producing empty files on nights with no data changes; users uploading placeholder/empty CSVs via the SIS import UI; SFTP drops with partial files.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/csv/import_refactored.rb:292

          csv ||= { file: parallel_importer.attachment.display_name }
          fail_with_error!(e, csv:)
        end
      ensure
        if !(@run_immediately || ensure_later) &&
           is_last_parallel_importer_of_type?(parallel_importer) &&
           !should_stop_import?
          queue_next_importer_set
        end
      end

      def try_importing_segment(input_csv, parallel_importer, importer_object, skip_progress: false)
        csv = input_csv || begin
          att = parallel_importer.attachment
          file = att.open(integrity_check: true)
          { fullpath: file.path, file: att.display_name }
        end
        parallel_importer.start
        raise "Empty file" if File.stat(csv[:fullpath]).size == 0

        count = importer_object.process(csv, parallel_importer.index, parallel_importer.batch_size)
        parallel_importer.complete(rows_processed: count)
        # just update progress on completion - the parallel jobs should be short enough
        update_progress unless skip_progress
      ensure
        file&.close
      end

      def fail_with_error!(e, csv: nil)
        return @batch if @batch.workflow_state == "aborted"

        message = "Importing CSV for account: " \
                  "#{@root_account.id} (#{@root_account.name}) sis_batch_id: #{@batch.id}: #{e}"
        err_id = Canvas::Errors.capture(e, {
                                          type: :sis_import,
                                          message:,
                                          during_tests: false

View on GitHub (pinned to 1c9f0bb801)