{"record":{"id":"9b30cf9cd63ba200","repo":"tonhowtf/omniget","slug":"could-not-extract-post-id-from-threads-url","errorCode":null,"errorMessage":"Could not extract post ID from Threads URL","messagePattern":"Could not extract post ID from Threads URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/threads.rs","lineNumber":383,"sourceCode":"        \"threads\"\n    }\n\n    fn can_handle(&self, url: &str) -> bool {\n        if let Ok(parsed) = url::Url::parse(url) {\n            if let Some(host) = parsed.host_str() {\n                let host = host.to_lowercase();\n                return host == \"threads.net\"\n                    || host.ends_with(\".threads.net\")\n                    || host == \"threads.com\"\n                    || host.ends_with(\".threads.com\");\n            }\n        }\n        false\n    }\n\n    async fn get_media_info(&self, url: &str) -> anyhow::Result<MediaInfo> {\n        let post_id = Self::extract_post_id(url)\n            .ok_or_else(|| anyhow!(\"Could not extract post ID from Threads URL\"))?;\n\n        let filename_base = format!(\"threads_{}\", post_id);\n\n        // Fetch della pagina e ricerca del post nel JSON bootstrap\n        let post = self.fetch_post(url, &post_id).await?;\n\n        // Per post share/repost il media vive in un contenitore annidato\n        let container = Self::resolve_media_container(&post);\n        let thumbnail_url = Self::best_thumbnail(container);\n\n        // Estrai metadata\n        let (uploader, _timestamp) = Self::extract_metadata(&post);\n\n        // Estrai media\n        let media = Self::extract_media_from_post(container)?;\n\n        match media {\n            ThreadsMedia::Single {","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/threads.rs#L365-L401","documentation":"get_media_info in threads.rs calls Self::extract_post_id(url) and uses ok_or_else to raise this error when the URL does not yield a post ID. It is an input validation failure: the supplied URL is not a Threads post URL the regex/pattern recognizes.","triggerScenarios":"Calling get_media_info with a URL that is not a Threads post link — a profile URL, a share link with an unexpected shape, a truncated URL, or a link to another platform.","commonSituations":"Users pasting threads.com/thread/... variants (threads.net vs threads.com domains), share links (threads.com/t/...) the extractor misses, or URLs with extra query/fragment parts breaking the pattern.","solutions":["Ensure the URL is a full Threads post URL containing a post ID","Add the missing URL shape (e.g. /t/ share links or threads.net domain) to extract_post_id","Normalize the URL (strip query params, map domains) before calling","Validate the URL client-side and reject non-post links with a clear message"],"exampleFix":"// before\nErr(anyhow!(\"Could not extract post ID from Threads URL\"))\n// after\nErr(anyhow!(\"Could not extract post ID from Threads URL: {}\", url))","handlingStrategy":"validation","validationCode":"const THREADS_POST_RE = /^https:\\/\\/(www\\.)?threads\\.((com)|(net))\\/@[\\w.]+\\/(post|t)\\/([\\w-]+)/;\nif (!THREADS_POST_RE.test(url)) throw new Error(\"Not a Threads post URL\");","typeGuard":"function isThreadsPostUrl(url) {\n  try { return THREADS_POST_RE.test(new URL(url).href); } catch { return false; }\n}","tryCatchPattern":"match threads.get_media_info(url).await {\n    Err(e) if e.to_string().contains(\"Could not extract post ID\") => {\n        return Err(UserInputError::new(\"Please paste a direct Threads post link\"));\n    }\n    other => other?,\n}","preventionTips":["Normalize domains (threads.net -> threads.com) and strip query strings before calling","Support all known share-link shapes (/t/, /post/) in your own pre-validation","Reject clearly non-post URLs in the UI before hitting the library","Keep the post-ID pattern in sync with extract_post_id"],"tags":["threads","url-parsing","validation"],"backgroundTag":"invalid-url-format","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"}