tonhowtf/omniget · error
resposta em lote inesperada do Twitch GQL
Error message
resposta em lote inesperada do Twitch GQL
What it means
persisted() sends the batched persisted-query format (a JSON array) and expects an array response whose first element is the page payload. It throws when the response is not an array (or is empty), i.e. the batch shape Twitch normally returns came back in an unexpected form.
Solutions
- Update the persisted query hash / client version to match the current Twitch site
- Retry after a short delay — can be transient
- Inspect the raw response to confirm whether it's an error object or HTML interstitial
- Fall back to the regular (non-persisted) query path if the library offers one
Example fix
// before
let raw = gql.persisted("Comments", OLD_HASH, vars).await?;
// after
let raw = match gql.persisted("Comments", CURRENT_HASH, vars).await {
Ok(r) => r,
Err(e) if e.to_string().contains("lote inesperada") => {
tracing::warn!("persisted query shape changed; check hash/headers");
return Err(e);
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Cannot validate remotely; keep the persisted query hash current with the Twitch site.
let hash_ok = PERSISTED_HASH.len() == 64
&& PERSISTED_HASH.chars().all(|c| c.is_ascii_hexdigit()); Type guard
fn is_batch_response(json: &serde_json::Value) -> bool {
json.as_array().map_or(false, |a| !a.is_empty())
} Try / catch
match client.persisted(op, hash, vars).await {
Err(e) if e.to_string().contains("lote inesperada") => {
tracing::warn!("batch shape changed — refresh hash/headers");
Err(e)
}
other => other,
} Prevention
- Keep persisted query hashes synced with the current Twitch web client
- Include the headers (Client-Id, etc.) the site sends
- Retry briefly — batch-shape errors can be intermittent
- Fall back to the non-persisted query path where available
When it happens
Trigger: fetch_all (VOD chat via persisted query) when the endpoint responds with a single JSON object instead of a batch array, an empty array, or an error object — commonly when the persisted query hash is outdated or blocked.
Common situations: Twitch changing/retiring a persisted query hash (client out of date); bot protection returning a non-batch error; intermittent Twitch API incidents; hitting the endpoint without required Client-Integrity headers.
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 do Twitch GQL sem `data`
- resposta sem comentários (o VOD tem replay de chat?)
- canal não encontrado
- VOD não encontrado (ou já expirou)
- clipe não encontrado
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/7c836309d5253cff.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/twitch/gql.rs:196
}
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> {
let q = format!(
r#"{{ user(login: "{}") {{ id login displayName profileImageURL(width: 300) }} }}"#,
escape(login)
);
let data = self.query(&q).await?;
let u = data.get("user").filter(|v| !v.is_null());
let u = u.ok_or_else(|| anyhow!("canal não encontrado: {}", login))?;
Ok(Channel {
id: str_at(u, "id"),
login: str_at(u, "login"),
display_name: str_at(u, "displayName"),
avatar: str_at(u, "profileImageURL"),
})
}
View on GitHub (pinned to 8600b91f42)