{"record":{"id":"4529b1efaef945c9","repo":"tonhowtf/omniget","slug":"post-not-available-graphql-syndication-html","errorCode":null,"errorMessage":"Post not available; graphql='{}'; syndication='{}'; html='{}'","messagePattern":"Post not available; graphql='(.+?)'; syndication='(.+?)'; html='(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/twitter.rs","lineNumber":948,"sourceCode":"                                        \"Post not available; graphql='{}'; syndication_extract='{}'; html='{}'\",\n                                        graphql_err,\n                                        syndication_extract_err,\n                                        html_err\n                                    ));\n                                }\n                            }\n                        }\n                    },\n                    Err(syndication_err) => {\n                        tracing::warn!(\n                            \"[twitter] syndication lookup failed for tweet_id={}: {}\",\n                            tweet_id,\n                            syndication_err\n                        );\n                        match self.request_html_media(url).await {\n                            Ok(items) => items,\n                            Err(html_err) => {\n                                return Err(anyhow!(\n                                    \"Post not available; graphql='{}'; syndication='{}'; html='{}'\",\n                                    graphql_err,\n                                    syndication_err,\n                                    html_err\n                                ));\n                            }\n                        }\n                    }\n                }\n            }\n        };\n\n        let twitter_media = Self::parse_media_items(&media_items)?;\n\n        Ok(Self::media_info_from_twitter_media(\n            filename_base,\n            twitter_media,\n        ))","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/twitter.rs#L930-L966","documentation":"The sibling of error 1033: here the syndication endpoint itself fails (request_syndication returns Err rather than its extraction step), GraphQL already failed, and the HTML-scrape fallback also fails, so native_get_media_info aggregates the three channel errors ('graphql', 'syndication', 'html') and returns. It means no extraction channel could retrieve or parse any media for the tweet.","triggerScenarios":"try_graphql errors (guest token unavailable, expired refresh also failed, HTTP error), request_syndication errors outright (network failure, syndication API non-2xx, timeout), and request_html_media errors (HTTP non-success or no photo URLs in HTML).","commonSituations":"Deleted or protected tweets; syndication CDN endpoint down or geo-blocked; Twitter aggressively rate-limiting anonymous traffic (all three channels are unauthenticated without a cookie); corporate proxy/firewall blocking api.twitter.com, cdn.syndication.twimg.com, and x.com HTML simultaneously; guest-token issuance failing so GraphQL errors immediately.","solutions":["Verify the tweet is public and still exists in a browser.","Set the X/Twitter auth cookie so the HTML path (and yt-dlp fallback) can authenticate and bypass anonymous rate limits.","Inspect the three embedded sub-errors for HTTP status codes: 429 means back off and retry; 403/404 means the post is unavailable or blocked.","Check network/proxy reachability to api.twitter.com, cdn.syndication.twimg.com, and x.com.","Retry with backoff; if persistent, rely on the outer get_media_info yt-dlp fallback and ensure the yt-dlp binary is up to date."],"exampleFix":"// before: immediate retry loop hammering rate-limited endpoints\nloop { downloader.get_media_info(url).await?; }\n// after: honor rate limits with exponential backoff\nfor delay in [1u64, 5, 30] {\n    match downloader.get_media_info(url).await {\n        Ok(info) => break info,\n        Err(e) if e.to_string().contains(\"429\") => {\n            tokio::time::sleep(Duration::from_secs(delay)).await;\n        }\n        Err(e) => return Err(e),\n    }\n} else { anyhow::bail!(\"unavailable after retries\") }","handlingStrategy":"retry","validationCode":"// Probe reachability of the three endpoints before extraction\nasync fn endpoints_reachable(client: &reqwest::Client) -> bool {\n    for u in [\"https://api.twitter.com\", \"https://cdn.syndication.twimg.com\", \"https://x.com\"] {\n        if client.head(u).send().await.is_err() { return false; }\n    }\n    true\n}","typeGuard":null,"tryCatchPattern":"match downloader.get_media_info(url).await {\n    Err(e) if e.to_string().contains(\"Post not available\") => {\n        if e.to_string().contains(\"429\") {\n            tokio::time::sleep(Duration::from_secs(60)).await;\n            // retry once\n        } else {\n            eprintln!(\"post unavailable or network blocked: {e}\");\n        }\n    }\n    other => other?,\n}","preventionTips":["Add exponential backoff on 429/5xx from the embedded sub-errors.","Set the auth cookie to reduce dependence on anonymous guest tokens and HTML scraping.","Verify network/proxy allows api.twitter.com, cdn.syndication.twimg.com, and x.com.","Treat repeated aggregate failures for the same ID as 'post gone' rather than retrying indefinitely.","Keep yt-dlp updated so the outer fallback remains a viable second chance."],"tags":["twitter","scraping","fallback-exhausted","rate-limit"],"backgroundTag":"api-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"}