{"record":{"id":"c107493e2a037230","repo":"tonhowtf/omniget","slug":"download-de-falhou-http","errorCode":null,"errorMessage":"download de {} falhou: HTTP {}","messagePattern":"download de (.+?) falhou: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/mod.rs","lineNumber":169,"sourceCode":"            .timeout(std::time::Duration::from_secs(600))\n            .build()?,\n    )\n}\n\n/// Baixa uma URL para um arquivo, em streaming, reportando bytes.\npub async fn download_to(\n    client: &reqwest::Client,\n    url: &str,\n    dest: &std::path::Path,\n    progress: &ProgressFn,\n    id: &str,\n) -> anyhow::Result<u64> {\n    use futures::StreamExt;\n    use tokio::io::AsyncWriteExt;\n\n    let resp = client.get(url).send().await?;\n    if !resp.status().is_success() {\n        anyhow::bail!(\"download de {} falhou: HTTP {}\", url, resp.status());\n    }\n    let total = resp.content_length();\n    if let Some(parent) = dest.parent() {\n        std::fs::create_dir_all(parent)?;\n    }\n    let part = dest.with_extension(\"part\");\n    let mut file = tokio::fs::File::create(&part).await?;\n    let mut stream = resp.bytes_stream();\n    let mut done: u64 = 0;\n    let mut last = std::time::Instant::now();\n    report(progress, id, \"started\", 0, total, None);\n    while let Some(chunk) = stream.next().await {\n        let chunk = chunk?;\n        file.write_all(&chunk).await?;\n        done += chunk.len() as u64;\n        if last.elapsed() > std::time::Duration::from_millis(200) {\n            report(progress, id, \"progress\", done, total, None);\n            last = std::time::Instant::now();","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/mod.rs#L151-L187","documentation":"HTTP status guard in download_to: the streaming download request for `url` completed but the server answered a non-2xx status, so no body is written to `dest` and the bail carries the failing URL and status code.","triggerScenarios":"Any `download_to(client, url, dest)` call where `resp.status().is_success()` is false: 404 (URL gone), 403 (forbidden/hotlink protection), 429 (rate limit), 5xx (server error).","commonSituations":"Downloading an asset whose URL rotated or expired; server blocking non-browser user agents; mirror/CDN outage; scraping too fast and hitting throttling.","solutions":["Verify the URL resolves (curl -I) and fix or refresh it","Retry with exponential backoff, especially for 429/5xx","Add auth headers/cookies or a browser-like User-Agent if the server returns 403","Handle 404 by skipping or sourcing the file elsewhere"],"exampleFix":"// before\ndownload_to(&client, old_url, &dest).await?; // 404\n// after\nlet fresh_url = refresh_url(id).await?;\ndownload_to(&client, &fresh_url, &dest).await?;","handlingStrategy":"retry","validationCode":"// optional pre-check of URL liveness\nlet resp = client.head(url).send().await?;\nif !resp.status().is_success() {\n    return Err(format!(\"URL not downloadable: HTTP {}\", resp.status()));\n}","typeGuard":null,"tryCatchPattern":"match download_to(&client, url, &dest).await {\n    Ok(n) => info!(\"saved {} bytes\", n),\n    Err(e) if e.to_string().contains(\"falhou: HTTP 4\") => warn!(\"permanent failure, skip: {}\", e),\n    Err(e) if e.to_string().contains(\"falhou: HTTP 5\") => {\n        retry_with_backoff(3, || download_to(&client, url, &dest)).await?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Classify statuses: retry 429/5xx with backoff, skip 404, fix auth for 403","Send realistic User-Agent and required auth headers","Cap concurrent requests to avoid server throttling"],"tags":["network","http","download","rust"],"backgroundTag":"http-error-response","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"}