tonhowtf/omniget · error

Bluesky API retornou HTTP

Error message

Bluesky API retornou HTTP {}

What it means

Thrown in fetch_post when the public Bluesky getPostThread endpoint returns a non-2xx HTTP status. The error embeds the status code so the developer can see whether it was a 400, 404, 5xx, etc. It guards before JSON parsing since the body will not be a valid thread.

Solutions

  1. Inspect the logged HTTP status: 404/400 means the post link is invalid or deleted; retry is pointless.
  2. For 429, back off and retry later respecting rate limits.
  3. For 5xx, retry with backoff or rely on the yt-dlp fallback path in get_media_info.
  4. Verify the URL matches bsky.app/<handle>/post/<postId> format before calling.

Example fix

let response = self.client.get(&url).send().await?;
// after: handle retries for transient statuses
if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS {
    tokio::time::sleep(std::time::Duration::from_secs(5)).await;
    return Box::pin(self.fetch_post(user, post_id)).await;
}
if !response.status().is_success() {
    return Err(anyhow!("Bluesky API retornou HTTP {}", response.status()));
}
Defensive patterns

Strategy: retry

Validate before calling

let parsed = url::Url::parse(url)?;
let segs: Vec<&str> = parsed.path().split('/').filter(|s| !s.is_empty()).collect();
if !(segs.len() >= 4 && segs[0] == "profile" && segs[2] == "post") {
    return Err(anyhow!("not a bsky post URL"));
}

Try / catch

for attempt in 0..3 {
    match downloader.get_media_info(url).await {
        Ok(info) => break Ok(info),
        Err(e) if e.to_string().contains("HTTP 5") || e.to_string().contains("HTTP 429") => {
            tokio::time::sleep(Duration::from_secs(2u64 << attempt)).await;
        }
        Err(e) => break Err(e), // 4xx: don't retry
    }
}

Prevention

When it happens

Trigger: GET to https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread returns non-success: malformed at:// URI, deleted/private post, rate limiting (429), or AppView outage (5xx).

Common situations: Deleted or blocked post IDs, wrong PDS/handle in the URL, rate limits from scraping many posts, transient Bluesky API outages, or network proxies mangling the request.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/fec6de254bc6cfef. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/bluesky.rs:139

        let segments: Vec<&str> = parsed.path().split('/').filter(|s| !s.is_empty()).collect();
        if segments.len() >= 4 && segments[0] == "profile" && segments[2] == "post" {
            return Some((segments[1].to_string(), segments[3].to_string()));
        }
        None
    }

    async fn fetch_post(&self, user: &str, post_id: &str) -> anyhow::Result<serde_json::Value> {
        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 },

View on GitHub (pinned to 8600b91f42)