we-promise/sure · error · Import::Preflight::PreflightError

invalid_file_type

invalid_file_type

Error message

Invalid file type. Please upload a CSV file.

What it means

Import::Preflight#csv_file_upload_attributes (app/models/import/preflight.rb:190) returns HTTP 422 {error: 'invalid_file_type'} with message 'Invalid file type. Please upload a CSV file.' when the multipart part's content_type is not in Import::ALLOWED_CSV_MIME_TYPES = [text/csv, text/plain, application/vnd.ms-excel, application/csv] (app/models/import.rb:37). Unlike the NDJSON path there is no filename-extension fallback: only the declared part Content-Type is inspected, and the same check runs at import creation (app/controllers/api/v1/imports_controller.rb:87).

Source

Thrown at app/models/import/preflight.rb:395

        payload: {
          error: "content_too_large",
          message: "Content is too large. Maximum size is #{SureImport.max_ndjson_size / 1.megabyte}MB."
        }
      )
    end

    def invalid_sure_file_type_response
      Response.new(
        status: :unprocessable_entity,
        payload: {
          error: "invalid_file_type",
          message: "Invalid file type. Please upload a Sure NDJSON file."
        }
      )
    end

    def raise_response(response)
      raise PreflightError, response
    end

    def unsupported_import_type_response
      Response.new(
        status: :unprocessable_entity,
        payload: {
          error: "unsupported_import_type",
          message: "Preflight supports CSV import types and SureImport."
        }
      )
    end
end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Declare the part type explicitly: curl -F 'file=@export.csv;type=text/csv'
  2. Convert .xlsx/.tsv to CSV UTF-8 before uploading
  3. Renaming the file does not help on the CSV path — the part Content-Type must match the allowlist
  4. If the client cannot set part content types, send raw_file_content instead; that path skips the MIME check and only checks size

Example fix

# before
curl -F 'file=@export.csv' https://app/api/v1/imports
# → 422 invalid_file_type (part sent as application/octet-stream)

# after
curl -F 'file=@export.csv;type=text/csv' https://app/api/v1/imports
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED_CSV_TYPES = %w[text/csv text/plain application/vnd.ms-excel application/csv]
part = Faraday::FilePart.new(io, 'text/csv', 'import.csv') # never rely on client-default octet-stream
raise 'part type not allowed' unless ALLOWED_CSV_TYPES.include?('text/csv')

Type guard

def csv_part_allowed?(content_type)
  %w[text/csv text/plain application/vnd.ms-excel application/csv].include?(content_type.to_s)
end

Prevention

When it happens

Trigger: curl -F 'file=@export.csv' (the part defaults to application/octet-stream); uploading .xlsx (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet is not allowlisted, only legacy vnd.ms-excel); a .tsv sent as text/tab-separated-values; a JSON file sent to the CSV path.

Common situations: Scripts using curl, Python requests, or Faraday that never set an explicit part content type; Excel's 'Export as .xlsx' instead of 'CSV UTF-8'; Linux tools that sniff and send unusual MIME types.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/008c4e2bbead98e1. Report an issue: GitHub.