{"record":{"id":"4ad9e45980552963","repo":"zeroclaw-labs/zeroclaw","slug":"http-downloading-datasheet-from-url","errorCode":null,"errorMessage":"HTTP {} downloading datasheet from {url}","messagePattern":"HTTP (.+?) downloading datasheet from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-hardware/src/datasheet.rs","lineNumber":70,"sourceCode":"    /// Returns the path to the saved file.\n    pub async fn download_datasheet(\n        &self,\n        url: &str,\n        device_name: &str,\n    ) -> anyhow::Result<PathBuf> {\n        std::fs::create_dir_all(&self.datasheet_dir)?;\n\n        let filename = format!(\"{}.pdf\", device_name.to_lowercase().replace(' ', \"_\"));\n        let dest = self.datasheet_dir.join(&filename);\n\n        let client = reqwest::Client::builder()\n            .user_agent(\"ZeroClaw/0.1 (datasheet downloader)\")\n            .timeout(std::time::Duration::from_secs(30))\n            .build()?;\n\n        let response = client.get(url).send().await?;\n        if !response.status().is_success() {\n            anyhow::bail!(\n                \"HTTP {} downloading datasheet from {url}\",\n                response.status()\n            );\n        }\n        let bytes = response.bytes().await?;\n        std::fs::write(&dest, &bytes)?;\n\n        ::zeroclaw_log::record!(\n            INFO,\n            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note).with_attrs(\n                ::serde_json::json!({\"device\": device_name, \"path\": dest.display().to_string()})\n            ),\n            \"datasheet downloaded\"\n        );\n        Ok(dest)\n    }\n\n    /// List all locally cached datasheet filenames.","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-hardware/src/datasheet.rs#L52-L88","documentation":"`download_datasheet` GETs a component datasheet URL with reqwest (30s timeout, ZeroClaw user agent) and bails on any non-2xx status, embedding the HTTP status and URL. The status code is the diagnosis: 404 means the datasheet moved or the URL is wrong, 403 usually means bot/hotlink blocking, 5xx means vendor-side outage. DNS and timeout failures surface as different errors from `send()`, so this one is strictly an HTTP response status problem.","triggerScenarios":"Executing the datasheet tool with a vendor URL that 404s after the manufacturer reorganized their site; a 403 from portals that block non-browser user agents; 5xx during vendor downtime.","commonSituations":"Stale hardcoded datasheet URLs in tool configuration; datasheet portals behind bot protection; typos in the URL handed to the tool.","solutions":["Read the status in the message: for 404, find the datasheet's current URL on the vendor's product page and update the tool input","For 403, download the file manually in a browser and point the tool at a local or mirrored copy","Retry later for 5xx — the outage is on the vendor's side"],"exampleFix":"// before\ndownload_datasheet(\"https://vendor.com/old/lm741.pdf\").await?;\n// after — canonical URL from the vendor's current product page\ndownload_datasheet(\"https://www.ti.com/lit/ds/symlink/lm741.pdf\").await?;","handlingStrategy":"retry","validationCode":"// Pre-flight the URL cheaply before downloading:\nlet resp = client.head(url).send().await?;\nif !resp.status().is_success() {\n    anyhow::bail!(\"datasheet URL unreachable ({}): {url}\", resp.status());\n}","typeGuard":null,"tryCatchPattern":"let mut attempt = 0;\nloop {\n    match download_datasheet(url, &dest).await {\n        Ok(_) => break,\n        Err(e) if attempt < 2 && e.to_string().contains(\"HTTP 5\") => {\n            attempt += 1;\n            tokio::time::sleep(std::time::Duration::from_secs(2u64.pow(attempt))).await;\n        }\n        Err(e) => return Err(e), // 404/403 are permanent — do not retry\n    }\n}","preventionTips":["Pin canonical vendor datasheet URLs (the /lit/ds/ style links) instead of search-result URLs","Mirror frequently used datasheets locally so vendor outages and bot blocks cannot break runs","Check the embedded HTTP status first: 4xx is permanent, 5xx and timeouts are retryable"],"tags":["hardware","datasheet","http","download"],"backgroundTag":"http-download-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}