tonhowtf/omniget · error
Unsupported link
Error message
Unsupported link
What it means
Thrown in fetch_post when the Bluesky API returns error "InvalidRequest". The getPostThread call was rejected as malformed — typically the at:// URI (handle/post id) built from the input URL is invalid, so the link itself is not a usable Bluesky post.
Solutions
- Validate the post id looks like a valid atproto TID (13-char base32-ish string) before calling the API.
- Ensure the URL is a direct post link, not a profile or other bsky.app page.
- Resolve handle-to-DID properly if the API rejects handle-based at:// URIs.
- Show an 'unsupported link' message and reject the URL early in can_handle/parsing.
Example fix
// before
fn extract_user_and_post(url: &str) -> Option<(String, String)> {
...
}
// after: reject obviously malformed post ids early
if post_id.len() != 13 || !post_id.chars().all(|c| c.is_ascii_alphanumeric()) {
return None; // caller emits "Could not extract user and post_id"
} Defensive patterns
Strategy: validation
Validate before calling
let parsed = url::Url::parse(url)?;
let segs: Vec<&str> = parsed.path().split('/').filter(|s| !s.is_empty()).collect();
let ok = segs.len() >= 4 && segs[0] == "profile" && segs[2] == "post"
&& segs[3].len() == 13 && segs[3].chars().all(|c| c.is_ascii_alphanumeric());
if !ok { return Err(anyhow!("not a valid bsky post link")); } Type guard
fn is_bsky_post_url(url: &str) -> bool {
url::Url::parse(url).ok()
.filter(|u| u.host_str().map_or(false, |h| h == "bsky.app" || h.ends_with(".bsky.app")))
.and_then(|u| {
let s: Vec<&str> = u.path().split('/').filter(|x| !x.is_empty()).collect();
if s.len() >= 4 && s[0] == "profile" && s[2] == "post" { Some((s[1], s[3])) } else { None }
})
.map_or(false, |(_, id)| id.len() == 13 && id.chars().all(|c| c.is_ascii_alphanumeric()))
} Try / catch
if !is_bsky_post_url(url) {
return Err(anyhow!("unsupported link: expected bsky.app/profile/<user>/post/<id>"));
}
let info = downloader.get_media_info(url).await?; Prevention
- Only accept canonical bsky.app/<handle>/post/<id> links
- Reject profile pages and other bsky.app routes before calling the downloader
- Beware URL shorteners or copied links with trailing path fragments
When it happens
Trigger: The input URL parses as bsky.app but extract_user_and_post yields a handle or post id that doesn't form a valid at:// URI (e.g. custom domain handles, unusual path segments, or a non-post bsky.app link that passed can_handle).
Common situations: Links like bsky.app/profile/<did>/post/<id> with unexpected characters, shortened/altered URLs, or non-post bsky.app pages (profile pages, starter packs) reaching the downloader.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Could not extract user and post_id from URL
- Bluesky API retornou HTTP
- Post not available
- Erro da API
- Bluesky API retornou HTTP
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/b04e64eee358b89c.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/platforms/bluesky.rs:147
let uri = format!("at://{}/app.bsky.feed.post/{}", user, post_id);
let url = format!(
"{}?depth=0&parentHeight=0&uri={}",
API_BASE,
urlencoding::encode(&uri)
);
let response = self.client.get(&url).send().await?;
if !response.status().is_success() {
return Err(anyhow!("Bluesky API retornou HTTP {}", response.status()));
}
let json: serde_json::Value = response.json().await?;
if let Some(error) = json.get("error").and_then(|e| e.as_str()) {
return match error {
"NotFound" | "InternalServerError" => Err(anyhow!("Post not available")),
"InvalidRequest" => Err(anyhow!("Unsupported link")),
_ => Err(anyhow!("Erro da API: {}", error)),
};
}
Ok(json)
}
}
enum BlueskyMedia {
Video { hls_url: String },
Images { urls: Vec<String> },
Gif { url: String },
}
fn extract_media(embed: &serde_json::Value) -> Option<BlueskyMedia> {
let embed_type = embed.get("$type")?.as_str()?;
match embed_type {View on GitHub (pinned to 8600b91f42)