tonhowtf/omniget · error
resposta do Twitch GQL sem `data`
Error message
resposta do Twitch GQL sem `data`
What it means
The GQL client's query() posts a GraphQL request and, after checking for GraphQL errors, requires a top-level 'data' key. It throws this when the response JSON lacks 'data' entirely, meaning Twitch answered but not with a usable GraphQL payload. This guards callers from nil 'data' panics downstream.
Solutions
- Retry — often transient on Twitch's side
- Inspect the raw response body for errors/anti-bot interstitials
- Update the client (persisted query hashes / headers) if Twitch changed the API
- Check network/proxy configuration that might rewrite responses
Example fix
// before
let data = gql.channel("xqc").await?;
// after
let data = match gql.channel("xqc").await {
Ok(d) => d,
Err(e) if e.to_string().contains("sem `data`") => {
tracing::warn!("Twitch GQL malformed response, retrying");
gql.channel("xqc").await?
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Not checkable pre-call; verify connectivity to gql.twitch.tv and no proxy interference.
let reachable = std::net::TcpStream::connect("gql.twitch.tv:443").is_ok(); Type guard
fn has_data(json: &serde_json::Value) -> bool {
json.get("data").map_or(false, |d| !d.is_null())
} Try / catch
match client.query(&q).await {
Err(e) if e.to_string().contains("sem `data`") => {
// Retry once after a short delay; otherwise surface as API incident
tokio::time::sleep(Duration::from_secs(2)).await;
client.query(&q).await
}
other => other,
} Prevention
- Add small backoff retries for transient Twitch incidents
- Keep the client updated against Twitch GQL changes
- Avoid proxies/captive portals that rewrite responses
- Log the raw body when this fires to detect anti-bot interstitials
When it happens
Trigger: Any channel/video/clip_video call where the Twitch GQL endpoint returns JSON without 'data': e.g. an 'errors'-shaped response that first_error missed, an authentication/introspection-style response, or Twitch serving an anti-bot/HTML-adjacent JSON error.
Common situations: Twitch-side incidents or schema changes; requests flagged by bot protection returning malformed JSON; deprecated/legacy API clients hitting newer endpoints; proxy or captive portal returning error JSON.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- resposta em lote inesperada do Twitch GQL
- Twitch GQL respondeu HTTP
- Twitch GQL
- resposta sem comentários (o VOD tem replay de chat?)
- canal não encontrado
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/806d156c73c0d076.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/twitch/gql.rs:181
}
if !status.is_success() {
bail!("Twitch GQL respondeu HTTP {}", status);
}
return Ok(resp.json::<Value>().await?);
}
bail!("Twitch GQL não respondeu depois de 5 tentativas: {}", last)
}
/// Query anônima em texto (as públicas aceitam sem persisted query).
pub async fn query(&self, query: &str) -> anyhow::Result<Value> {
let body = json!({ "query": query });
let json = self.post(&body).await?;
if let Some(msg) = first_error(&json) {
bail!("Twitch GQL: {}", msg);
}
json.get("data")
.cloned()
.ok_or_else(|| anyhow!("resposta do Twitch GQL sem `data`"))
}
/// Persisted query (formato em lote, como o site manda). Devolve o
/// primeiro elemento cru, para quem quiser ler `errors` também.
pub async fn persisted(&self, op: &str, hash: &str, vars: Value) -> anyhow::Result<Value> {
let body = json!([{
"operationName": op,
"variables": vars,
"extensions": { "persistedQuery": { "version": 1, "sha256Hash": hash } },
}]);
let json = self.post(&body).await?;
json.as_array()
.and_then(|a| a.first())
.cloned()
.ok_or_else(|| anyhow!("resposta em lote inesperada do Twitch GQL"))
}
pub async fn channel(&self, login: &str) -> anyhow::Result<Channel> {View on GitHub (pinned to 8600b91f42)