{"record":{"id":"0be418593977c541","repo":"tonhowtf/omniget","slug":"could-not-extract-user-and-post-id-from-url","errorCode":null,"errorMessage":"Could not extract user and post_id from URL","messagePattern":"Could not extract user and post_id from URL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/bluesky.rs","lineNumber":33,"sourceCode":"    client: reqwest::Client,\n}\n\nimpl Default for BlueskyDownloader {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl BlueskyDownloader {\n    async fn fallback_ytdlp(&self, url: &str) -> anyhow::Result<MediaInfo> {\n        let ytdlp_path = crate::core::ytdlp::ensure_ytdlp().await?;\n        let json = crate::core::ytdlp::get_video_info(&ytdlp_path, url, &[]).await?;\n        crate::platforms::generic_ytdlp::GenericYtdlpDownloader::parse_video_info(&json)\n    }\n\n    async fn native_get_media_info(&self, url: &str) -> anyhow::Result<MediaInfo> {\n        let (user, post_id) = Self::extract_user_and_post(url)\n            .ok_or_else(|| anyhow!(\"Could not extract user and post_id from URL\"))?;\n\n        let json = self.fetch_post(&user, &post_id).await?;\n\n        let embed = json\n            .pointer(\"/thread/post/embed\")\n            .ok_or_else(|| anyhow!(\"Post does not contain media\"))?;\n\n        let media = extract_media(embed).ok_or_else(|| anyhow!(\"Unsupported media type\"))?;\n\n        let filename_base = format!(\"bluesky_{}_{}\", sanitize_filename::sanitize(&user), post_id);\n\n        match media {\n            BlueskyMedia::Video { hls_url } => Ok(MediaInfo {\n                title: filename_base,\n                author: user,\n                platform: \"bluesky\".to_string(),\n                duration_seconds: None,\n                thumbnail_url: None,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/bluesky.rs#L15-L51","documentation":"native_get_media_info() for Bluesky extracts the handle and post id (rkey) from the URL via extract_user_and_post(). If the URL doesn't match the expected post-URL shape, it returns this error before making any network call. It's a URL-shape validation guard on the input link.","triggerScenarios":"get_media_info() -> native_get_media_info() with a bluesky URL that extract_user_and_post() cannot parse — missing /post/<rkey> segment, wrong domain, profile or search URL, or extra query fragments.","commonSituations":"Passing https://bsky.app/profile/<user> (profile page) instead of a post URL; a shortened/custom-domain link; a URL with a trailing locale or query string the regex doesn't tolerate.","solutions":["Use the full post URL copied from Bluesky: https://bsky.app/profile/<handle>/post/<rkey>.","Normalize custom-domain links to bsky.app form before passing them in.","Extend extract_user_and_post() to handle AT-URI form (at://...) or extra URL segments."],"exampleFix":"// before\nlet url_in = \"https://bsky.app/profile/alice.bsky.social\"; // profile URL -> error\n// after\nlet url_in = \"https://bsky.app/profile/alice.bsky.social/post/3kx2abc123\"; // full post URL","handlingStrategy":"validation","validationCode":"fn looks_like_bluesky_post(url: &str) -> bool {\n    url.contains(\"bsky.app/profile/\") && url.contains(\"/post/\")\n}\nif !looks_like_bluesky_post(url) { return Err(anyhow!(\"expected a bsky.app post URL\")); }","typeGuard":"fn parse_bsky_post(url: &str) -> Option<(String, String)> {\n    let rest = url.strip_prefix(\"https://bsky.app/profile/\")?;\n    let mut it = rest.split('/');\n    let user = it.next()?.to_string();\n    let (_post, rkey) = (it.next()?, it.next()?.to_string());\n    Some((user, rkey))\n}","tryCatchPattern":"match get_media_info(url).await {\n    Err(e) if e.to_string().contains(\"Could not extract user and post_id\") => {\n        eprintln!(\"use full post URL: https://bsky.app/profile/<handle>/post/<rkey>\");\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Always pass full post URLs containing /profile/<handle>/post/<rkey>.","Reject profile or search pages before calling get_media_info.","Normalize custom-domain links to bsky.app format first."],"tags":["bluesky","url-parsing","invalid-url"],"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"}