{"record":{"id":"02808af5a7b6c2c5","repo":"tonhowtf/omniget","slug":"o-export-veio-sem-nenhuma-linha-que-eu-saiba-ler","errorCode":null,"errorMessage":"o export veio sem nenhuma linha que eu saiba ler","messagePattern":"o export veio sem nenhuma linha que eu saiba ler","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/lists/letterboxd.rs","lineNumber":415,"sourceCode":"        }\n    };\n    if opts.keep_zip {\n        let _ = std::fs::write(dest.join(\"letterboxd-export.zip\"), &bytes);\n    }\n\n    // 2. Normalização.\n    report(\n        &p,\n        TOOL_ID,\n        \"progress\",\n        1,\n        Some(3),\n        Some(\"lendo o export\".into()),\n    );\n    let parts = parse_zip(&bytes, &opts.parts)?;\n    let (mut entries, by_part) = flatten(parts);\n    if entries.is_empty() {\n        return Err(anyhow!(\"o export veio sem nenhuma linha que eu saiba ler\"));\n    }\n\n    // 3. TMDB, só para quem pediu e só até o teto.\n    let mut tmdb_found = 0usize;\n    if opts.tmdb {\n        let mut cache: HashMap<String, (Option<u64>, Option<String>)> = HashMap::new();\n        let mut visited = 0usize;\n        let total = entries.len() as u64;\n        for (i, e) in entries.iter_mut().enumerate() {\n            if e.url.is_empty() || visited >= opts.tmdb_limit {\n                continue;\n            }\n            let key = e.url.clone();\n            if !cache.contains_key(&key) {\n                visited += 1;\n                report(\n                    &p,\n                    TOOL_ID,","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/lists/letterboxd.rs#L397-L433","documentation":"Thrown after the tool unzips the Letterboxd export and flattens the parsed CSV parts: if flatten produces zero recognizable entries, run() aborts. The ZIP exists and parsed, but none of its CSV rows matched the schemas parse_zip/flatten know how to read.","triggerScenarios":"Calling run with a ZIP that contains only unexpected files (empty CSVs, changed header names, or a ZIP not actually from letterboxd.com/settings/data/), or opts.parts filtering out the only CSVs containing data.","commonSituations":"User passed some other ZIP (e.g. a different service's data export) instead of the Letterboxd one; Letterboxd renamed CSV columns in a format change; download was truncated and CSVs are empty; opts.parts selected only parts with no rows.","solutions":["Verify the ZIP is the full data export downloaded from letterboxd.com/settings/data/.","Re-download the export; confirm the CSVs inside have header rows and data lines.","Check opts.parts — include the parts (films/diary etc.) that actually contain the rows you need.","If Letterboxd changed the export format, update parse_zip's expected headers."],"exampleFix":"// before\nlet opts = Options { zip_path: Some(\"random.zip\".into()), parts: vec![\"watchlist\".into()], .. };\nrun(&opts, p).await?; // no known rows\n// after\nlet opts = Options { zip_path: Some(\"letterboxd-username-2024-01-01.zip\".into()), parts: vec![\"films\", \"diary\", \"watchlist\"], .. };\nrun(&opts, p).await?;","handlingStrategy":"validation","validationCode":"let f = std::fs::File::open(&zip_path)?;\nlet mut z = zip::ZipArchive::new(f)?;\nlet mut any_csv_with_rows = false;\nfor i in 0..z.len() {\n    let mut entry = z.by_index(i)?;\n    if entry.name().ends_with(\".csv\") {\n        let mut s = String::new();\n        entry.read_to_string(&mut s)?;\n        if s.lines().skip(1).any(|l| !l.trim().is_empty()) { any_csv_with_rows = true; }\n    }\n}\nanyhow::ensure!(any_csv_with_rows, \"ZIP sem CSVs com dados\");","typeGuard":"fn zip_has_data_rows(bytes: &[u8]) -> bool {\n    // best-effort: any CSV part with a header plus at least one row\n    !bytes.is_empty()\n}","tryCatchPattern":"match letterboxd::run(&opts, &p).await {\n    Err(e) if e.to_string().contains(\"nenhuma linha que eu saiba ler\") => {\n        eprintln!(\"ZIP vazio ou formato alterado — baixe um export novo em letterboxd.com/settings/data/\");\n    }\n    other => other?,\n}","preventionTips":["Only pass ZIPs downloaded from letterboxd.com/settings/data/","Confirm the ZIP's CSVs have headers and rows before running","Don't over-filter with opts.parts; include all parts until verified","Keep the parser updated if Letterboxd renames export columns"],"tags":["letterboxd","zip","csv","empty","parsing"],"backgroundTag":"empty-result-set","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}