tonhowtf/omniget · error
Twitch GQL retornou HTTP
Error message
Twitch GQL retornou HTTP {} What it means
Raised by fetch_clip_metadata when the POST to https://gql.twitch.tv/gql returns a non-2xx HTTP status. The error embeds the status code, so it covers 4xx (bad client-id, malformed query) and 5xx (Twitch outage) cases.
Solutions
- Log/inspect response.status() and the response body to identify 4xx vs 5xx
- Retry with backoff on 429/5xx
- Update the CLIENT_ID constant to the current public web client-id
- Verify proxy/TLS configuration if 403s appear
Example fix
// before
if !response.status().is_success() {
return Err(anyhow!("Twitch GQL retornou HTTP {}", response.status()));
}
// after
if response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS {
tokio::time::sleep(Duration::from_secs(5)).await;
return self.fetch_clip_metadata(slug).await; // bounded retry
}
if !response.status().is_success() {
let body = response.text().await.unwrap_or_default();
return Err(anyhow!("Twitch GQL retornou HTTP {}: {}", body.len(), body));
} Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: probe GQL availability
let status = reqwest::Client::new().post("https://gql.twitch.tv/gql")
.header("client-id", "kimne78kx3ncx6brgo4mv6wki5h1ko")
.json(&serde_json::json!({"query":"{clip(slug:\"x\"){id}}"}))
.send().await?.status(); Try / catch
for attempt in 0..3 {
match downloader.get_media_info(url).await {
Err(e) if e.to_string().contains("Twitch GQL retornou HTTP 429")
|| e.to_string().contains("HTTP 5") => tokio::time::sleep(Duration::from_secs(2u64 << attempt)).await,
other => return other,
}
} Prevention
- Back off on 429/5xx with exponential delay
- Keep CLIENT_ID current with Twitch's public web client-id
- Avoid tight polling loops against GQL
- Check https://status.twitch.tv during suspected outages
When it happens
Trigger: Any failed GraphQL metadata request: network path returns 400/401 for an invalid client-id header or malformed query, 403 for blocked clients, 429 rate limiting, or 5xx during Twitch incidents.
Common situations: Twitch rotated/invalidated the hardcoded CLIENT_ID; rate-limited by excessive polling; corporate proxy intercepting requests; Twitch GQL outage.
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/9508855a8d8cfd09.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/platforms/twitch.rs:139
async fn fetch_clip_metadata(&self, slug: &str) -> anyhow::Result<ClipMetadata> {
let query = format!(
r#"{{ clip(slug: "{}") {{ broadcaster {{ login }} curator {{ login }} durationSeconds id medium: thumbnailURL(width: 480, height: 272) title videoQualities {{ quality sourceURL }} }} }}"#,
slug
);
let body = serde_json::json!({ "query": query });
let response = self
.client
.post(GQL_URL)
.header("client-id", CLIENT_ID)
.json(&body)
.send()
.await?;
if !response.status().is_success() {
return Err(anyhow!("Twitch GQL retornou HTTP {}", response.status()));
}
let json: serde_json::Value = response.json().await?;
let clip = json
.pointer("/data/clip")
.ok_or_else(|| anyhow!("Clip not found: {}", slug))?;
if clip.is_null() {
return Err(anyhow!("Clip not found: {}", slug));
}
let title = clip
.get("title")
.and_then(|v| v.as_str())
.unwrap_or("Untitled")
.to_string();
View on GitHub (pinned to 8600b91f42)