{"record":{"id":"abc032cacb8a9d51","repo":"tonhowtf/omniget","slug":"threads-returned-http","errorCode":null,"errorMessage":"Threads returned HTTP {}","messagePattern":"Threads returned HTTP (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/threads.rs","lineNumber":123,"sourceCode":"    async fn fetch_page(&self, url: &str, user_agent: &str) -> anyhow::Result<String> {\n        let response = self\n            .client\n            .get(url)\n            .header(\"User-Agent\", user_agent)\n            .header(\n                \"Accept\",\n                \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\n            )\n            .header(\"Accept-Language\", \"en-GB,en;q=0.9\")\n            .header(\"Sec-Fetch-Dest\", \"document\")\n            .header(\"Sec-Fetch-Mode\", \"navigate\")\n            .header(\"Sec-Fetch-Site\", \"none\")\n            .header(\"Sec-Fetch-User\", \"?1\")\n            .send()\n            .await?;\n\n        if !response.status().is_success() {\n            return Err(anyhow!(\"Threads returned HTTP {}\", response.status()));\n        }\n\n        Ok(response.text().await?)\n    }\n\n    /// Cerca il post nei <script data-sjs> della pagina. Threads cambia spesso\n    /// il nesting del bootstrap (require/__bbox/result/data/media), quindi invece\n    /// di un path fisso parsiamo ogni script e cerchiamo ricorsivamente\n    /// l'oggetto con il `code` del post e chiavi media.\n    fn find_post_in_html(html: &str, post_id: &str) -> Option<serde_json::Value> {\n        let re = Regex::new(r#\"(?s)<script[^>]*data-sjs[^>]*>(.*?)</script>\"#).ok()?;\n\n        for cap in re.captures_iter(html) {\n            let script_content = cap.get(1).map(|m| m.as_str()).unwrap_or(\"\");\n\n            if !script_content.contains(post_id) {\n                continue;\n            }","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/threads.rs#L105-L141","documentation":"fetch_page in threads.rs checks response.status().is_success() after fetching the Threads page and converts any non-2xx into this error, embedding the HTTP status. It is a thin wrapper turning an HTTP failure from threads.com into a library error, so callers know the page fetch itself failed rather than the parsing.","triggerScenarios":"fetch_post calls fetch_page (with either browser UA or Googlebot UA) and Threads responds 4xx/5xx — e.g. 403 from bot detection, 404 for an unknown post, 429 rate limiting, or 5xx server errors.","commonSituations":"Threads' bot protection rejecting the client's UA/TLS fingerprint (403); scraping too aggressively (429); posting a typo'd URL pointing at a nonexistent post (404); transient Threads outages (5xx).","solutions":["Read the embedded status: 403/429 means throttle or change User-Agent; 404 means the post does not exist","Back off and retry with exponential delay for 429/5xx","Use a residential proxy or different egress IP if 403 persists","Validate the URL points to an existing public post before calling"],"exampleFix":"// before\nErr(anyhow!(\"Threads returned HTTP {}\", response.status()))\n// after\nErr(anyhow!(\"Threads returned HTTP {}; {}\", response.status(),\n    if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS { \"rate limited, retry later\" } else { \"check URL/UA\" }))","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match threads.get_media_info(url).await {\n    Err(e) if e.to_string().contains(\"Threads returned HTTP\") => {\n        let status = extract_status(&e);\n        if matches!(status, 429 | 500..=599) { retry_with_backoff(); }\n        else { surface_error(\"Thread unavailable (check URL)\"); }\n    }\n    other => other?,\n}","preventionTips":["Rate-limit requests to Threads and add exponential backoff on 429/5xx","Rotate User-Agents and consider residential proxies for 403 blocks","Validate post URLs exist before fetching","Handle each HTTP class (4xx vs 5xx) distinctly in callers"],"tags":["threads","http","scraping"],"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"}