{"record":{"id":"740bc56a24735a0a","repo":"Freika/dawarich","slug":"could-not-classify-zip-contents-file-may-be-cor","errorCode":null,"errorMessage":"Could not classify zip contents -- file may be corrupted","messagePattern":"Could not classify zip contents -- file may be corrupted","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/services/imports/create.rb","lineNumber":114,"sourceCode":"  def importer(source)\n    raise ArgumentError, I18n.t('services.imports.create.source_missing') if source.nil?\n\n    case source.to_s\n    when 'google_semantic_history'      then GoogleMaps::SemanticHistoryImporter\n    when 'google_phone_takeout'         then GoogleMaps::PhoneTakeoutImporter\n    when 'google_records'               then GoogleMaps::RecordsStorageImporter\n    when 'google_photos'                then GooglePhotos::Importer\n    when 'owntracks'                    then OwnTracks::Importer\n    when 'gpx'                          then Gpx::TrackImporter\n    when 'kml'                          then Kml::Importer\n    when 'geojson'                      then Geojson::Importer\n    when 'immich_api', 'photoprism_api' then Photos::Importer\n    when 'csv'                          then Csv::Importer\n    when 'tcx'                          then Tcx::Importer\n    when 'fit'                          then Fit::Importer\n    when 'polarsteps'                   then Polarsteps::Importer\n    when 'zip'\n      raise ArgumentError, I18n.t('services.imports.create.zip_unclassified')\n    else\n      raise ArgumentError, I18n.t('services.imports.create.unsupported_source', source:)\n    end\n  end\n\n  def update_import_points_count(import)\n    Import::UpdatePointsCountJob.perform_later(import.id)\n  end\n\n  def notify_if_all_skipped(import)\n    import.reload\n    return unless import.points.count.zero?\n\n    if import.doubles.to_i.positive?\n      I18n.with_locale(import.user.locale) do\n        Notification.create!(\n          user_id: import.user_id,\n          title: I18n.t('services.imports.create.import_completed_with_no_new_points'),","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/services/imports/create.rb#L96-L132","documentation":"ArgumentError raised by Imports::Create#importer when the resolved import source is the literal string 'zip'. A .zip upload is supposed to be unpacked and re-dispatched by Archive::Unzipper.inspect_archive (:multi_entry goes to ZipExtractor, :single_entry extracts the inner file and re-detects); reaching importer('zip') means the archive's contents fit neither shape, so no concrete importer exists to run and the service refuses with 'Could not classify zip contents -- file may be corrupted'.","triggerScenarios":"Uploading a file with a .zip extension and valid PK\\x03\\x04 magic bytes whose entries are all unrecognized by the unzipper's classification (no importable inner files), a zip whose central directory is damaged so inspect_archive returns neither :multi_entry nor :single_entry, or an effectively empty zip that still passes zip_file?.","commonSituations":"Partially downloaded or interrupted Google Takeout zips, archives re-packed by tools that wrap files in an unexpected layout, re-uploading an already-extracted export, or an Import record whose source column was manually set to 'zip'.","solutions":["Verify the zip opens locally (unzip -t) and re-download/re-create it if damaged","Extract the zip manually and upload the meaningful inner file directly (Records.json, timeline.json, .gpx, .tcx, etc.)","For multi-file Takeout zips, make sure recognizable entries (Records.json / timeline JSONs) are actually inside","Check server logs for the Archive::Unzipper.inspect_archive result to see why neither dispatch kind matched"],"exampleFix":"# before: uploading an arbitrary .zip and expecting Dawarich to figure it out\nImport.create!(source: 'zip', file: my_mystery_zip)\n\n# after: extract and upload the inner location file directly\n# unzip takeout.zip -d takeout && upload takeout/Takeout/Location History/Records.json\nImport.create!(file: File.open('takeout/Records.json'))  # source auto-detected","handlingStrategy":"validation","validationCode":"# Classify the archive BEFORE creating/processing the import\nkind = Archive::Unzipper.inspect_archive(zip_path).kind\nunless %i[multi_entry single_entry].include?(kind)\n  raise ArgumentError, \"Zip contents unclassifiable (kind=#{kind.inspect}); extract it and upload the inner file directly\"\nend","typeGuard":"def classifiable_zip?(path)\n  return false unless File.extname(path).downcase == '.zip'\n  %i[multi_entry single_entry].include?(Archive::Unzipper.inspect_archive(path).kind)\nend","tryCatchPattern":"begin\n  Imports::Create.new(user, import).call\nrescue ArgumentError => e\n  import.update!(status: :failed, error_message: e.message) # Imports::Create already does this; hook here for custom UX\nend","preventionTips":["Validate zip classifiability at upload time, not during the background job","Test archives locally with unzip -t before uploading","Upload inner files (Records.json, .gpx) directly instead of zipping when possible"],"tags":["import","zip","file-validation","dawarich"],"backgroundTag":"corrupt-zip-archive","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}