tonhowtf/omniget · error
o arquivo baixado não parece o export do Goodreads
Error message
o arquivo baixado não parece o export do Goodreads
What it means
This error is thrown by trigger_and_wait in the Goodreads export tool after it downloads the CSV generated by Goodreads' import/export pipeline. The tool polls until the export is ready, fetches the file, and sanity-checks that the body contains the string 'title' (a required column header in a Goodreads library export). If the downloaded text does not look like a Goodreads CSV, it assumes something else came back (HTML error page, login page, empty export) and throws this anyhow error.
Solutions
- Check that the Goodreads session cookies are valid: re-capture cookies from goodreads.com in the browser extension and pass them via opts.session_netscape.
- Trigger the export manually at goodreads.com/review/import and download goodreads_library_export.csv, then pass its path via opts.csv_path so the download/poll path is skipped entirely.
- Increase opts.delay_ms / re-run to give Goodreads more time to finish generating the export before the fetch.
- Inspect what was actually downloaded (log the first bytes of csv) to see if it is an HTML page or an empty file and adjust accordingly.
Example fix
// before: rely solely on polling the generated export
let (csv, secs) = trigger_and_wait(&f, opts, &p).await?;
// after: prefer an existing local export when present
if let Some(path) = opts.csv_path.as_deref().map(str::trim) {
let csv = std::fs::read_to_string(path)?;
...
} else {
let (csv, secs) = trigger_and_wait(&f, opts, &p).await?;
...
} Defensive patterns
Strategy: validation
Validate before calling
if let Some(path) = &opts.csv_path {
let head = std::fs::read_to_string(path)?;
anyhow::ensure!(head.to_lowercase().contains("title"), "arquivo não parece export do Goodreads");
} Type guard
fn looks_like_goodreads_csv(body: &str) -> bool {
body.to_lowercase().contains("title")
} Try / catch
match tool::run(&opts, &p).await {
Ok(res) => println!("export ok: {}", res.output_dir),
Err(e) if e.to_string().contains("export do Goodreads") => {
eprintln!("Goodreads devolveu algo inesperado; re-capture os cookies ou gere o CSV manualmente");
}
Err(e) => return Err(e),
} Prevention
- Keep a manually downloaded goodreads_library_export.csv handy and pass csv_path to skip the fragile polling path
- Re-capture Goodreads cookies right before running; expired sessions yield HTML instead of CSV
- Give the export time to finish (larger delay_ms) before the tool fetches the file
- Log the first bytes of any failed download to distinguish HTML pages from empty files
When it happens
Trigger: Calling run without a local CSV path so trigger_and_wait waits for Goodreads to generate the export, then f.get_bytes(&csv_url) returns content whose lowercase form lacks 'title' — e.g. Goodreads returned an HTML/login/error page at the export URL, the export job produced an empty or partial file, or the poll loop timed out and grabbed a not-yet-ready resource.
Common situations: Goodreads export job never completed but the URL was fetched anyway; the session cookies expired so Goodreads served a sign-in page; Goodreads changed its export format or column names; network middleware (proxy/CAPTCHA page) intercepted the download.
Related errors
- formato desconhecido: {}
- formato desconhecido: {}
- o Goodreads recusou o pedido de export (HTTP {}). Ele só dei
- o Goodreads respondeu HTTP {} no arquivo do export
- escolha a pasta de destino
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/ab7e46b22850c66d.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/lists/goodreads.rs:326
"o Goodreads ainda não gerou o CSV depois de {}s. Deixe goodreads.com/review/import aberto, espere e rode de novo — o link fica salvo lá",
limit.as_secs()
));
}
report(
p,
TOOL_ID,
"progress",
started.elapsed().as_secs(),
Some(limit.as_secs()),
Some("esperando o Goodreads gerar o CSV".into()),
);
tokio::time::sleep(Duration::from_secs(10)).await;
}
let (bytes, _) = f.get_bytes(&csv_url).await?;
let csv = String::from_utf8_lossy(&bytes).to_string();
if !csv.to_lowercase().contains("title") {
return Err(anyhow!(
"o arquivo baixado não parece o export do Goodreads"
));
}
Ok((csv, started.elapsed().as_secs()))
}
/// A página devolvida quando a sessão não vale: o Goodreads responde 200 com
/// a tela de entrada em vez de mandar para outro lugar.
pub fn is_signed_out(html: &str) -> bool {
let has_form = html.contains("review_porter") || html.contains("js-LibraryExport");
!has_form && (html.contains("/user/sign_in") || html.contains("/user/new"))
}
// ── Prateleira na web ───────────────────────────────────────────────────
pub fn shelf_url(user: &str, shelf: &str, page: u32) -> String {
format!(
"https://www.goodreads.com/review/list/{}?shelf={}&per_page=100&page={}&print=true",View on GitHub (pinned to 8600b91f42)