Freika/dawarich · error · Imports::SourceDetector::UnknownSourceError

Unable to detect file format

Error message

Unable to detect file format

What it means

Imports::SourceDetector::UnknownSourceError raised by detect_source! when every detection stage returns nil: extension+magic-byte checks (gpx/kml/kmz/zip/fit/tcx/csv/owntracks .rec), JSON structure rules in DETECTION_RULES, and the raw-content fallback detect_from_raw_content. The message comes from unsupported_reason, which tries to name the specific payload (encrypted Timeline backup, HTML page, Snapchat export, saved places, etc.) and falls back to the generic 'Unable to detect file format' when nothing matches.

Source

Thrown at app/services/imports/source_detector.rb:129

    json_data = parse_json

    if json_data
      DETECTION_RULES.each do |format, rules|
        next if format == :owntracks # Already handled above

        return format if matches_format?(json_data, rules)
      end
    end

    # Fallback: detect from raw content when JSON parsing fails (e.g. deeply nested truncation)
    detect_from_raw_content
  end

  def detect_source!
    format = detect_source
    return format if format

    raise UnknownSourceError, unsupported_reason
  end

  private

  attr_reader :file_content, :filename, :file_path

  def gpx_file?
    return false unless filename

    # Must have .gpx extension AND contain GPX XML structure
    return false unless filename.downcase.end_with?('.gpx')

    # Check content for GPX structure
    content_to_check =
      if file_path && File.exist?(file_path)
        # Read first 1KB for GPX detection
        File.open(file_path, 'rb') { |f| f.read(1024) }
      else

View on GitHub (pinned to 97fad417c5)

Solutions

  1. Read the exact message - unsupported_reason usually names the payload (encrypted_timeline, html_page, my_activity, saved_places...) and tells you what you actually uploaded
  2. Re-export from the source app choosing the correct dataset (Location History -> Records.json or timeline JSON, not My Activity / Saved Places)
  3. For encrypted timelines, disable encryption in the source app before exporting
  4. Convert the data to a supported format (GeoJSON, CSV, GPX) and upload that instead

Example fix

# before: blind call that raises
source = Imports::SourceDetector.new_from_file_header(path).detect_source!

# after: soft-detect first and branch on nil
source = Imports::SourceDetector.new_from_file_header(path).detect_source
if source.nil?
  Rails.logger.warn('File not recognized; ask user to check export instructions')
else
  importer(source).new(import, user.id, path).call
end
Defensive patterns

Strategy: validation

Validate before calling

detector = Imports::SourceDetector.new_from_file_header(path)
source = detector.detect_source   # non-bang: returns nil instead of raising
if source.nil?
  Rails.logger.warn('Unrecognized import file; not creating a failed import')
  return :unrecognized
end

Type guard

def recognized_import_file?(path)
  !Imports::SourceDetector.new_from_file_header(path).detect_source.nil?
end

Try / catch

begin
  source = detector.detect_source!
rescue Imports::SourceDetector::UnknownSourceError => e
  # e.message already names the payload (encrypted timeline, HTML page, ...)
  render json: { error: e.message }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: Uploading a file with no detection rule: Google 'My Activity' JSON, Saved Places export, Amazon order history, Snapchat data export, an encrypted Google Timeline backup ('You have encrypted Timeline backups'), an HTML login/error page saved with a .json extension, an empty {} / [] file, or a truncated JSON fragment. Only the first 8KB are parsed as JSON (262KB for raw matching), so files whose identifying keys appear later can also miss.

Common situations: Picking the wrong file out of a large Google Takeout bundle, exporting with timeline encryption enabled, a download link that returned HTML instead of data, or a source app shipping a new export shape the detector has not learned yet.

Related errors


AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21). Data as JSON: /api/errors/1f65f5cd992842b5. Report an issue: GitHub.