{"record":{"id":"2080efc808876e48","repo":"tonhowtf/omniget","slug":"twitter-extraction-failed-native-ytdlp-mod","errorCode":null,"errorMessage":"Twitter extraction failed. native='{}'; ytdlp='{}'","messagePattern":"Twitter extraction failed\\. native='(.+?)'; ytdlp='(.+?)'","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/platforms/twitter/mod.rs","lineNumber":830,"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":812,"sourceCodeEnd":848,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/twitter/mod.rs#L812-L848","documentation":"get_media_info tries native Twitter extraction first and falls back to yt-dlp; this error is raised only when BOTH strategies fail, aggregating both underlying messages into one string. The native error and the yt-dlp error are the real diagnostic payload embedded in the formatted message.","triggerScenarios":"native_get_media_info fails (bad URL, GraphQL/syndication blocked, tweet unavailable) AND the subsequent yt-dlp fallback also fails (yt-dlp missing/outdated, network failure, unsupported tweet) — both in one get_media_info call.","commonSituations":"Twitter/X restricting guest access to GraphQL so native fails, combined with an outdated yt-dlp binary that also fails on new X layouts; offline environments; rate limiting hitting both paths; malformed tweet URLs.","solutions":["Read both embedded errors: fix the native cause first (usually auth/rate-limit or unavailable tweet) as indicated by native='...'.","Update yt-dlp to the latest version since stale binaries fail on current X responses.","Verify the URL is a valid tweet URL extractable to a tweet ID.","Retry later if the errors indicate rate limiting (HTTP 429) from Twitter endpoints.","Ensure yt-dlp is installed and on PATH for the fallback path to be meaningful."],"exampleFix":"// before\nErr(anyhow!(\"Twitter extraction failed. native='{}'; ytdlp='{}'\", native_err, fallback_err))\n// after\n#[derive(Debug, thiserror::Error)]\nenum TwitterExtractError {\n    #[error(\"native extraction failed: {0}\")] Native(#[source] anyhow::Error),\n    #[error(\"yt-dlp fallback failed: {0}\")] Ytdlp(#[source] anyhow::Error),\n}\nreturn Err(TwitterExtractError::Ytdlp(fallback_err).into()); // preserves both causes as a chain","handlingStrategy":"try-catch","validationCode":"// pre-flight checks before calling get_media_info\nfn preflight_ok(url: &str, ytdlp_path: &str) -> Result<(), String> {\n    if extract_tweet_id(url).is_none() { return Err(\"not a tweet URL\".into()); }\n    if !std::path::Path::new(ytdlp_path).exists() { return Err(\"yt-dlp not found\".into()); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match get_media_info(url).await {\n    Ok(info) => download(info),\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"429\") { schedule_retry_with_backoff(); }\n        else if msg.contains(\"ytdlp=\") && msg.contains(\"not found\") { prompt_ytdlp_install(); }\n        else { show_extraction_failed(&msg); }\n    }\n}","preventionTips":["Keep yt-dlp updated; both paths failing is often a stale binary plus guest-token trouble.","Validate the tweet URL before calling extraction.","Back off and retry on rate-limit signatures in either embedded error.","Monitor both native and fallback failure rates separately to know which path is degrading."],"tags":["twitter","yt-dlp","fallback-exhausted","extraction"],"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"}