{"record":{"id":"a2e906606b20e974","repo":"tonhowtf/omniget","slug":"twitch-gql","errorCode":null,"errorMessage":"Twitch GQL: {}","messagePattern":"Twitch GQL: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/twitch/gql.rs","lineNumber":177,"sourceCode":"            let status = resp.status();\n            if status.as_u16() == 429 || status.is_server_error() {\n                last = format!(\"HTTP {}\", status);\n                continue;\n            }\n            if !status.is_success() {\n                bail!(\"Twitch GQL respondeu HTTP {}\", status);\n            }\n            return Ok(resp.json::<Value>().await?);\n        }\n        bail!(\"Twitch GQL não respondeu depois de 5 tentativas: {}\", last)\n    }\n\n    /// Query anônima em texto (as públicas aceitam sem persisted query).\n    pub async fn query(&self, query: &str) -> anyhow::Result<Value> {\n        let body = json!({ \"query\": query });\n        let json = self.post(&body).await?;\n        if let Some(msg) = first_error(&json) {\n            bail!(\"Twitch GQL: {}\", msg);\n        }\n        json.get(\"data\")\n            .cloned()\n            .ok_or_else(|| anyhow!(\"resposta do Twitch GQL sem `data`\"))\n    }\n\n    /// Persisted query (formato em lote, como o site manda). Devolve o\n    /// primeiro elemento cru, para quem quiser ler `errors` também.\n    pub async fn persisted(&self, op: &str, hash: &str, vars: Value) -> anyhow::Result<Value> {\n        let body = json!([{\n            \"operationName\": op,\n            \"variables\": vars,\n            \"extensions\": { \"persistedQuery\": { \"version\": 1, \"sha256Hash\": hash } },\n        }]);\n        let json = self.post(&body).await?;\n        json.as_array()\n            .and_then(|a| a.first())\n            .cloned()","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/twitch/gql.rs#L159-L195","documentation":"The public anonymous query helper checks the GQL response for a GraphQL `errors` array (via first_error) and, if present, wraps the first error message in this error. HTTP succeeded, but Twitch rejected the operation at the GraphQL level — bad query syntax, unknown fields, or a data-layer error such as a nonexistent resource. It surfaces the server-side GraphQL error text directly to the caller.","triggerScenarios":"Calling GqlClient::query with a query string that Twitch accepts over HTTP but fails to execute — unknown field/operation, syntax error in the query, variables mismatch, or entity not found — causing a non-empty `errors` array in the JSON response.","commonSituations":"Twitch renamed/removed fields from its public GQL schema so channel/video/clip queries reference nonexistent fields; typos in hand-written GraphQL strings; querying private/deleted channels or videos; using a persisted operation shape with the anonymous query endpoint.","solutions":["Read the message text — it contains Twitch's own GraphQL error, usually naming the offending field or problem.","Validate the query against the current Twitch GQL schema (introspection or a GQL playground) and fix removed/renamed fields.","Check the entity being queried (channel login, video/clip id) actually exists and is public.","If Twitch rotated its schema, update the query strings in channel/video/clip_video to the new shape.","Consider adding fallback logic that inspects the full `errors` array instead of only the first message."],"exampleFix":"// before\nif let Some(msg) = first_error(&json) {\n    bail!(\"Twitch GQL: {}\", msg);\n}\n// after\nif let Some(msg) = first_error(&json) {\n    bail!(\"Twitch GQL: {} (query: {}...)\", msg, &query[..query.len().min(80)]);\n}","handlingStrategy":"try-catch","validationCode":"// check the entity exists / query is non-empty before calling\nif query.trim().is_empty() { anyhow::bail!(\"empty GQL query\"); }","typeGuard":null,"tryCatchPattern":"match gql.query(&q).await {\n    Err(e) if e.to_string().starts_with(\"Twitch GQL:\") => {\n        // GraphQL-level failure: parse message, fix schema fields or entity id\n    }\n    other => other,\n}","preventionTips":["Lint GraphQL queries against Twitch's current schema in CI (schema diff or gql lint).","Verify channel/video/clip identifiers exist and are public before querying.","Treat Twitch GQL schema drift as a maintenance item; pin known-good query strings.","Inspect the full `errors` array, not just the first message, when debugging."],"tags":["twitch","graphql","api","schema"],"backgroundTag":"api-error-response","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}