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

  1. Validate the post id looks like a valid atproto TID (13-char base32-ish string) before calling the API.
  2. Ensure the URL is a direct post link, not a profile or other bsky.app page.
  3. Resolve handle-to-DID properly if the API rejects handle-based at:// URIs.
  4. 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

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


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)