{"record":{"id":"2e85c1760b2fbf01","repo":"tonhowtf/omniget","slug":"post-not-available-graphql-syndication-extract-html-mod","errorCode":null,"errorMessage":"Post not available; graphql='{}'; syndication_extract='{}'; html='{}'","messagePattern":"Post not available; graphql='(.+?)'; syndication_extract='(.+?)'; html='(.+?)'","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/platforms/twitter/mod.rs","lineNumber":996,"sourceCode":"            Err(graphql_err) => {\n                tracing::warn!(\n                    \"[twitter] graphql lookup failed for tweet_id={}: {}\",\n                    tweet_id,\n                    graphql_err\n                );\n                match self.request_syndication(&tweet_id).await {\n                    Ok(syndication) => match Self::extract_syndication_media(&syndication) {\n                        Ok(items) => items,\n                        Err(syndication_extract_err) => {\n                            tracing::warn!(\n                                \"[twitter] syndication media extraction failed for tweet_id={}: {}\",\n                                tweet_id,\n                                syndication_extract_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_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) => {","sourceCodeStart":978,"sourceCodeEnd":1014,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/twitter/mod.rs#L978-L1014","documentation":"native_get_media_info tries three strategies in order: GraphQL API, syndication JSON extraction, and HTML scraping (request_html_media). This error is raised only when all three fail, combining each strategy's error message into a single string. It typically means the post is unavailable (deleted, private, age-restricted) or Twitter is blocking all guest access paths.","triggerScenarios":"GraphQL call fails (auth/rate-limit/429 or missing tweet), syndication extraction fails (error/empty payload), and HTML scrape fails (login wall, changed markup) — all within one native_get_media_info call on the same tweet URL.","commonSituations":"Deleted or protected tweets, age-restricted content requiring an X login, Twitter hardening guest access so GraphQL/syndication return errors simultaneously, regional blocks, and markup changes breaking the HTML scraper during X UI updates.","solutions":["Check whether the tweet is deleted, protected, or age-restricted in a browser — this error frequently means the post genuinely is not accessible anonymously.","Inspect each embedded error (graphql / syndication_extract / html) for rate limiting (429) and retry after backoff if so.","Update yt-dlp and fall back to it when all native strategies fail.","Add authenticated cookie/token support for age-restricted or login-walled content.","Update the HTML scraper selectors if html='...' shows a parse/selector failure after an X UI change."],"exampleFix":"// before\nreturn Err(anyhow!(\"Post not available; graphql='{}'; syndication_extract='{}'; html='{}'\", graphql_err, syndication_extract_err, html_err));\n// after\nif [graphql_err.to_string(), syndication_extract_err.to_string(), html_err.to_string()]\n    .iter().any(|e| e.contains(\"429\")) {\n    return Err(anyhow!(\"Post temporarily unavailable (rate limited); retry with backoff\"));\n}\nreturn Err(anyhow!(\"Post not available; graphql='{graphql_err}'; syndication_extract='{syndication_extract_err}'; html='{html_err}'\"));","handlingStrategy":"retry","validationCode":"// check availability early and detect retryable upstream failures\nasync fn tweet_reachable(url: &str) -> bool {\n    // HEAD/GET the syndication endpoint; false likely means deleted/private/restricted\n    reqwest::Client::new().head(syndication_url_for(url)).send().await\n        .map(|r| r.status().is_success()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match native_get_media_info(url).await {\n    Ok(info) => download(info),\n    Err(e) => {\n        let m = e.to_string();\n        if m.contains(\"429\") { retry_with_backoff(3); }\n        else if m.contains(\"Post not available\") { notify_user_post_unavailable(); /* offer yt-dlp fallback */ }\n        else { return Err(e); }\n    }\n}","preventionTips":["Check in a browser that the tweet is public and not age-restricted before debugging extraction.","Retry with exponential backoff when any embedded error shows 429/rate limiting.","Keep a yt-dlp fallback ready for guest-access blocks and login walls.","Add authenticated cookies for age-restricted content.","Watch for X UI/markup changes that break the HTML strategy and update selectors promptly."],"tags":["twitter","fallback-exhausted","post-unavailable","graphql","scraping"],"backgroundTag":"upstream-api-error","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"}