{"record":{"id":"429f7d59426fa8cb","repo":"tonhowtf/omniget","slug":"failed-to-download-attachment","errorCode":null,"errorMessage":"Failed to download attachment: {}","messagePattern":"Failed to download attachment: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/course_utils.rs","lineNumber":81,"sourceCode":"\n    let path = format!(\"{}/{}\", dir, filename);\n\n    if Path::new(&path).exists() {\n        let meta = std::fs::metadata(&path);\n        if meta.map(|m| m.len() > 0).unwrap_or(false) {\n            return Ok(0);\n        }\n    }\n\n    if cancel_token.is_cancelled() {\n        return Err(anyhow!(\"Download cancelled\"));\n    }\n\n    let resp = client\n        .get(url)\n        .send()\n        .await\n        .map_err(|e| anyhow!(\"Failed to download attachment: {}\", e))?;\n\n    if !resp.status().is_success() {\n        return Err(anyhow!(\n            \"Attachment download failed: HTTP {}\",\n            resp.status()\n        ));\n    }\n\n    let bytes = resp.bytes().await?;\n    let size = bytes.len() as u64;\n\n    if size == 0 {\n        return Ok(0);\n    }\n\n    let part_path = format!(\"{}.part\", path);\n    std::fs::write(&part_path, &bytes)?;\n    std::fs::rename(&part_path, &path)?;","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/course_utils.rs#L63-L99","documentation":"download_attachment performs an HTTP GET for a course attachment via reqwest. If the request fails at the transport level (connection error, DNS failure, TLS error, timeout), the reqwest::Error is wrapped as \"Failed to download attachment: {}\". The download never got an HTTP response at all.","triggerScenarios":"client.get(url).send().await returns Err — network unreachable, DNS resolution failure, TLS handshake failure, connection reset, or request timeout — while downloading a course attachment.","commonSituations":"Offline or flaky network; VPN/proxy interference; firewall blocking the course host; misconfigured DNS; server temporarily down; TLS certificate problems on the endpoint.","solutions":["Check network connectivity to the attachment host (curl -v the URL to reproduce).","Retry with backoff — transport errors are frequently transient.","Verify proxy/VPN settings and that reqwest client config (timeouts, TLS features) matches the environment.","Inspect the wrapped reqwest::Error message for the specific cause (DNS vs connect vs timeout) and fix that layer."],"exampleFix":"// before\nlet resp = client.get(url).send().await\n    .map_err(|e| anyhow!(\"Failed to download attachment: {}\", e))?;\n\n// after\nlet resp = client.get(url).send().await\n    .map_err(|e| anyhow!(\"Failed to download attachment: {}\", e))\n    .or_else(|e| async move {\n        tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n        client.get(url).send().await\n            .map_err(|e2| anyhow!(\"Failed to download attachment after retry: {}\", e2))\n    })\n    .await?;","handlingStrategy":"retry","validationCode":"// Cheap reachability probe before the download\nlet host_ok = reqwest::Client::new().head(url).send().await.is_ok();\nif !host_ok { eprintln!(\"Host unreachable; check network before retrying\"); }","typeGuard":"fn is_transport_error(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"Failed to download attachment\")\n}","tryCatchPattern":"let mut attempt = 0;\nloop {\n    match download_attachment(&client, &url, &dest, &token).await {\n        Err(e) if e.to_string().contains(\"Failed to download attachment\") && attempt < 3 => {\n            attempt += 1;\n            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;\n        }\n        other => break other,\n    }\n}","preventionTips":["Configure reqwest client timeouts and connection pooling sensibly","Retry transport errors with exponential backoff","Check network/proxy/VPN state in deployment environments","Log the inner reqwest::Error cause to distinguish DNS vs connect vs timeout"],"tags":["network","http","download","reqwest","retryable"],"backgroundTag":"http-request-failed","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"}