{"record":{"id":"87b8d7b7ff6b25cb","repo":"tonhowtf/omniget","slug":"twitter-extraction-failed-native-ytdlp","errorCode":null,"errorMessage":"Twitter extraction failed. native='{}'; ytdlp='{}'","messagePattern":"Twitter extraction failed\\. native='(.+?)'; ytdlp='(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/twitter.rs","lineNumber":764,"sourceCode":"        false\n    }\n\n    async fn get_media_info(&self, url: &str) -> anyhow::Result<MediaInfo> {\n        match self.native_get_media_info(url).await {\n            Ok(info) => Ok(info),\n            Err(native_err) => {\n                tracing::warn!(\n                    \"[twitter] native failed: {}, trying yt-dlp fallback\",\n                    native_err\n                );\n                match self.fallback_ytdlp(url).await {\n                    Ok(info) => Ok(info),\n                    Err(fallback_err) => {\n                        tracing::warn!(\n                            \"[twitter] yt-dlp fallback failed after native error: {}\",\n                            fallback_err\n                        );\n                        Err(anyhow!(\n                            \"Twitter extraction failed. native='{}'; ytdlp='{}'\",\n                            native_err,\n                            fallback_err\n                        ))\n                    }\n                }\n            }\n        }\n    }\n\n    async fn download(\n        &self,\n        info: &MediaInfo,\n        opts: &DownloadOptions,\n        progress: mpsc::Sender<ProgressUpdate>,\n    ) -> anyhow::Result<DownloadResult> {\n        if let Some(quality) = info.available_qualities.first() {\n            if quality.format == \"ytdlp\" {","sourceCodeStart":746,"sourceCodeEnd":782,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/twitter.rs#L746-L782","documentation":"get_media_info first tries the native Twitter extraction (guest-token GraphQL + syndication + HTML fallbacks) and, if that fails, falls back to shelling out to yt-dlp. This error is thrown only when BOTH strategies fail; the message embeds the native error and the yt-dlp error so the root cause of each path is preserved in a single aggregate error.","triggerScenarios":"Calling get_media_info(url) where native_get_media_info returns Err (tweet unavailable, no media, tweet ID not extractable) AND fallback_ytdlp also returns Err (yt-dlp binary missing or failed to launch, yt-dlp out of date for current Twitter/X layout, network failure, or the tweet itself is gone/protected).","commonSituations":"Deleted or private tweets (both paths legitimately fail); outdated yt-dlp binary after a Twitter/X frontend change; missing or expired auth cookie so native extraction gets empty media and yt-dlp hits login walls; offline or blocked network; corrupted yt-dlp installation via ensure_ytdlp.","solutions":["Read the two embedded sub-errors: fix the more actionable one first (native= usually points at tweet availability, ytdlp= at the tool or network).","Update yt-dlp to the latest version (`yt-dlp -U` or re-run ensure_ytdlp) — Twitter extractors change frequently.","Configure the X/Twitter auth cookie (auth_cookie_string) so both native and yt-dlp paths can access restricted content.","Open the URL in a browser to confirm the tweet is public and still exists.","If native= is a schema error, update the extract_* functions in twitter.rs; if ytdlp= is a launcher error, verify the yt-dlp binary path and network access."],"exampleFix":"// before: retrying get_media_info blindly\nlet info = downloader.get_media_info(url).await?;\n// after: check tweet availability and refresh yt-dlp first\nif !tweet_exists_in_browser(url) {\n    anyhow::bail!(\"tweet is deleted or private\");\n}\ncrate::core::ytdlp::ensure_ytdlp_updated().await?;\nlet info = downloader.get_media_info(url).await?;","handlingStrategy":"try-catch","validationCode":"// before calling, ensure yt-dlp is available and URL is a live public tweet\nlet ytdlp_path = ensure_ytdlp().await?; // fails fast if binary cannot be procured\nif !url.contains(\"/status/\") {\n    anyhow::bail!(\"not a tweet permalink\");\n}","typeGuard":null,"tryCatchPattern":"match downloader.get_media_info(url).await {\n    Err(e) if e.to_string().starts_with(\"Twitter extraction failed.\") => {\n        // both native and yt-dlp failed; parse embedded sub-errors\n        let native = e.to_string();\n        eprintln!(\"all strategies exhausted: {native}\");\n        // escalate to user: check tweet existence / update yt-dlp\n    }\n    other => other?,\n}","preventionTips":["Keep yt-dlp current; schedule periodic updates since Twitter extractors break often.","Configure the auth cookie so both paths can access restricted content.","Validate the tweet is public and exists before invoking extraction.","Log the embedded native= and ytdlp= sub-errors separately for diagnosis.","Avoid retry storms: back off on rate-limit-looking sub-errors."],"tags":["twitter","yt-dlp","fallback-exhausted","network"],"backgroundTag":"api-request-failed","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"}