{"record":{"id":"126f6a4d5ab94199","repo":"tonhowtf/omniget","slug":"o-goodreads-respondeu-http-no-arquivo-do-export","errorCode":null,"errorMessage":"o Goodreads respondeu HTTP {} no arquivo do export","messagePattern":"o Goodreads respondeu HTTP (.+?) no arquivo do export","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/lists/goodreads.rs","lineNumber":301,"sourceCode":"            .await?;\n        let status = resp.status();\n        if !status.is_success() && status.as_u16() != 302 {\n            return Err(anyhow!(\n                \"o Goodreads recusou o pedido de export (HTTP {}). Ele só deixa gerar um a cada poucos dias\",\n                status\n            ));\n        }\n    }\n\n    // Espera: o CSV é gerado em segundo plano e responde 404 até ficar pronto.\n    let limit = Duration::from_secs(opts.wait_secs.clamp(30, 3600));\n    loop {\n        let code = f.head_status(&csv_url).await.unwrap_or(0);\n        if code == 200 {\n            break;\n        }\n        if code != 404 && code != 0 && code != 403 {\n            return Err(anyhow!(\n                \"o Goodreads respondeu HTTP {} no arquivo do export\",\n                code\n            ));\n        }\n        if started.elapsed() >= limit {\n            return Err(anyhow!(\n                \"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á\",\n                limit.as_secs()\n            ));\n        }\n        report(\n            p,\n            TOOL_ID,\n            \"progress\",\n            started.elapsed().as_secs(),\n            Some(limit.as_secs()),\n            Some(\"esperando o Goodreads gerar o CSV\".into()),\n        );","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/lists/goodreads.rs#L283-L319","documentation":"While polling the export CSV URL with HEAD requests, trigger_and_wait tolerates 404 (not ready yet), 0 (request failure), and 403 (temporarily forbidden) and keeps waiting. Any other non-200 status is treated as a hard failure of the export file endpoint and surfaces this error.","triggerScenarios":"head_status returning a status like 410, 500, 502, or 302-that-isn't-followed on the CSV URL: the export link expired/was deleted (a newer export overwrote it), Goodreads server error, or an intermediary/proxy erroring.","commonSituations":"A previous export link saved on the import page was invalidated because someone triggered a new export; Goodreads outage returning 5xx; account state changed mid-poll (e.g. signed out elsewhere invalidating the link).","solutions":["Check whether a new export was triggered recently — generating a new one deletes the old file; wait for the new export to finish and rerun.","Retry after a few minutes if the status was 5xx — this is often a transient Goodreads server error.","Recapture cookies and rerun if the link appears permanently gone (404 would be tolerated, so an exotic code usually means session/link invalidation).","Verify general Goodreads availability in a browser to rule out an outage on their side."],"exampleFix":"// before\n// transient 502 kills the run\nlet code = f.head_status(&csv_url).await.unwrap_or(0);\n\n// after (caller-level retry)\nfor _ in 0..3 {\n    match run(&f, &opts).await {\n        Ok(v) => return Ok(v),\n        Err(e) if e.to_string().contains(\"HTTP 5\") => tokio::time::sleep(Duration::from_secs(120)).await,\n        Err(e) => return Err(e),\n    }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match run(&fetcher, &opts).await {\n    Err(e) if e.to_string().contains(\"no arquivo do export\") => {\n        // 5xx from Goodreads is often transient; retry after a delay\n        tokio::time::sleep(Duration::from_secs(300)).await;\n        run(&fetcher, opts).await?;\n    }\n    r => r?,\n}","preventionTips":["Avoid triggering a second export while one is pending (it deletes the old file)","Retry on 5xx after a cooldown","Keep the browser session alive during polling"],"tags":["http","polling","goodreads","upstream"],"backgroundTag":"unexpected-http-status","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"}