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

  1. Retry — often transient on Twitch's side
  2. Inspect the raw response body for errors/anti-bot interstitials
  3. Update the client (persisted query hashes / headers) if Twitch changed the API
  4. 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

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


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)