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
- Inspect the logged HTTP status: 404/400 means the post link is invalid or deleted; retry is pointless.
- For 429, back off and retry later respecting rate limits.
- For 5xx, retry with backoff or rely on the yt-dlp fallback path in get_media_info.
- 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
- Validate the bsky.app post URL shape (profile/<handle>/post/<id>) before calling
- Distinguish retryable statuses (429, 5xx) from permanent ones (400, 404) in your catch logic
- Respect Bluesky public API rate limits; throttle batch downloads
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)