{"record":{"id":"1f65f5cd992842b5","repo":"Freika/dawarich","slug":"unable-to-detect-file-format","errorCode":null,"errorMessage":"Unable to detect file format","messagePattern":"Unable to detect file format","errorType":"exception","errorClass":"Imports::SourceDetector::UnknownSourceError","httpStatus":null,"severity":"error","filePath":"app/services/imports/source_detector.rb","lineNumber":129,"sourceCode":"    json_data = parse_json\n\n    if json_data\n      DETECTION_RULES.each do |format, rules|\n        next if format == :owntracks # Already handled above\n\n        return format if matches_format?(json_data, rules)\n      end\n    end\n\n    # Fallback: detect from raw content when JSON parsing fails (e.g. deeply nested truncation)\n    detect_from_raw_content\n  end\n\n  def detect_source!\n    format = detect_source\n    return format if format\n\n    raise UnknownSourceError, unsupported_reason\n  end\n\n  private\n\n  attr_reader :file_content, :filename, :file_path\n\n  def gpx_file?\n    return false unless filename\n\n    # Must have .gpx extension AND contain GPX XML structure\n    return false unless filename.downcase.end_with?('.gpx')\n\n    # Check content for GPX structure\n    content_to_check =\n      if file_path && File.exist?(file_path)\n        # Read first 1KB for GPX detection\n        File.open(file_path, 'rb') { |f| f.read(1024) }\n      else","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/services/imports/source_detector.rb#L111-L147","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Re-export from the source app choosing the correct dataset (Location History -> Records.json or timeline JSON, not My Activity / Saved Places)","For encrypted timelines, disable encryption in the source app before exporting","Convert the data to a supported format (GeoJSON, CSV, GPX) and upload that instead"],"exampleFix":"# before: blind call that raises\nsource = Imports::SourceDetector.new_from_file_header(path).detect_source!\n\n# after: soft-detect first and branch on nil\nsource = Imports::SourceDetector.new_from_file_header(path).detect_source\nif source.nil?\n  Rails.logger.warn('File not recognized; ask user to check export instructions')\nelse\n  importer(source).new(import, user.id, path).call\nend","handlingStrategy":"validation","validationCode":"detector = Imports::SourceDetector.new_from_file_header(path)\nsource = detector.detect_source   # non-bang: returns nil instead of raising\nif source.nil?\n  Rails.logger.warn('Unrecognized import file; not creating a failed import')\n  return :unrecognized\nend","typeGuard":"def recognized_import_file?(path)\n  !Imports::SourceDetector.new_from_file_header(path).detect_source.nil?\nend","tryCatchPattern":"begin\n  source = detector.detect_source!\nrescue Imports::SourceDetector::UnknownSourceError => e\n  # e.message already names the payload (encrypted timeline, HTML page, ...)\n  render json: { error: e.message }, status: :unprocessable_entity\nend","preventionTips":["Pre-flight detect_source (bang-free) at upload time and reject early with guidance","Keep export instructions per source app (exact Takeout file names) near the upload UI","Never save an HTML login page as .json and upload it - validate content type server-side"],"tags":["import","format-detection","file-validation"],"backgroundTag":"unsupported-file-format","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}