{"record":{"id":"86f17f94b5b4e842","repo":"tonhowtf/omniget","slug":"failed-to-read-thumbnail","errorCode":null,"errorMessage":"Failed to read thumbnail: {}","messagePattern":"Failed to read thumbnail: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/ffmpeg.rs","lineNumber":591,"sourceCode":"    dest_dir: &Path,\n) -> anyhow::Result<std::path::PathBuf> {\n    let response = client\n        .get(url)\n        .send()\n        .await\n        .map_err(|e| anyhow!(\"Failed to download thumbnail: {}\", e))?;\n\n    let content_type = response\n        .headers()\n        .get(\"content-type\")\n        .and_then(|v| v.to_str().ok())\n        .unwrap_or(\"image/jpeg\")\n        .to_string();\n\n    let bytes = response\n        .bytes()\n        .await\n        .map_err(|e| anyhow!(\"Failed to read thumbnail: {}\", e))?;\n\n    let ext = if content_type.contains(\"png\") {\n        \"png\"\n    } else {\n        \"jpg\"\n    };\n\n    let thumb_path = dest_dir.join(format!(\".omniget_thumb_{}.{}\", uuid::Uuid::new_v4(), ext));\n    std::fs::write(&thumb_path, &bytes)?;\n\n    if ext == \"png\" {\n        let jpg_path = dest_dir.join(format!(\".omniget_thumb_{}.jpg\", uuid::Uuid::new_v4()));\n        let convert_result = crate::core::process::command(\"ffmpeg\")\n            .args([\n                \"-y\",\n                \"-i\",\n                &thumb_path.to_string_lossy(),\n                &jpg_path.to_string_lossy(),","sourceCodeStart":573,"sourceCodeEnd":609,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/ffmpeg.rs#L573-L609","documentation":"After a successful HTTP response, download_thumbnail reads the full response body as bytes. If reading the body fails (connection dropped mid-transfer, timeout while streaming body, decompression error), the reqwest error is wrapped with this message. The status was OK but the payload never arrived intact.","triggerScenarios":"resp.bytes().await fails because the server closed the connection mid-body, a read timeout expired, or the response stream errored.","commonSituations":"Flaky mobile/proxied networks; CDN aborting large responses; keep-alive connections reset by intermediaries; server-side timeouts on slow clients.","solutions":["Retry the download once or twice; transient body read failures are common.","Increase the client's read/total timeout if thumbnails are large or the network is slow.","Check that no intermediary (proxy/VPN/firewall) is truncating responses.","Fall back to skipping the thumbnail embed instead of failing the whole download."],"exampleFix":"// before\nlet bytes = response.bytes().await\n    .map_err(|e| anyhow!(\"Failed to read thumbnail: {}\", e))?;\n// after\nlet bytes = match response.bytes().await {\n    Ok(b) => b,\n    Err(e) => {\n        log::warn!(\"thumbnail body read failed: {e}\");\n        return Ok(None);\n    }\n};","handlingStrategy":"retry","validationCode":"// Check Content-Length availability before reading\nlet expected = response.content_length();\nif expected == Some(0) { return Ok(None); }","typeGuard":null,"tryCatchPattern":"let bytes = response.bytes().await\n    .or_else(|_| async { client.get(url).send().await?.bytes().await }) // one retry\n    .await\n    .map_err(|e| { log::warn!(\"thumb body failed: {e}\"); e })?;","preventionTips":["Configure generous read timeouts for image downloads.","Retry transient body errors once before giving up.","Avoid proxies/VPN known to truncate streaming responses.","Degrade to no-thumbnail instead of failing the pipeline."],"tags":["network","http","thumbnail","body-read"],"backgroundTag":"network-request-failed","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}