{"record":{"id":"19515f62f3d2ca4c","repo":"tonhowtf/omniget","slug":"twitch-gql-n-o-respondeu-depois-de-5-tentativas","errorCode":null,"errorMessage":"Twitch GQL não respondeu depois de 5 tentativas: {}","messagePattern":"Twitch GQL não respondeu depois de 5 tentativas: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/twitch/gql.rs","lineNumber":169,"sourceCode":"                .await;\n            let resp = match sent {\n                Ok(r) => r,\n                Err(e) => {\n                    last = e.to_string();\n                    continue;\n                }\n            };\n            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!([{","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/twitch/gql.rs#L151-L187","documentation":"This error is raised after the Twitch GQL client exhausts its 5 retry attempts without receiving a successful response. On each attempt the client records the last failure reason (429 rate limit or server error) in `last`; when the loop ends it bails with that reason embedded. It means Twitch remained unavailable or rate-limited across all retries — a persistent transient failure, not a single bad request.","triggerScenarios":"Any call through post (query or persisted query) where all 5 attempts return HTTP 429 or a 5xx status, so the retry loop never reaches the success path.","commonSituations":"Hammering Twitch's GQL endpoint without backoff and hitting sustained rate limits; Twitch GQL outage or maintenance window; IP-level throttling (datacenter/VPN IPs are heavily limited); misconfigured retry interval too short for the rate-limit window to reset.","solutions":["Add exponential backoff with jitter between retry attempts instead of retrying immediately.","Inspect `last` in the message: if HTTP 429, slow down call rate, cache results, and respect Twitch rate limits.","Route requests through a different IP/proxy if the current IP is persistently throttled.","Increase the attempt count and/or add a longer cool-down for server errors (5xx) during Twitch outages.","Surface the error to the caller with the recorded `last` status so users know it was a rate limit vs server error."],"exampleFix":"// before\nfor _ in 0..5 {\n    // attempt request, on 429/5xx: last = ...; continue;\n}\nbail!(\"Twitch GQL não respondeu depois de 5 tentativas: {}\", last)\n// after\nfor attempt in 0..5 {\n    // attempt request...\n    tokio::time::sleep(Duration::from_millis(500 * 2u64.pow(attempt))).await;\n}\nbail!(\"Twitch GQL não respondeu depois de 5 tentativas: {}\", last)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match gql.query(&q).await {\n    Ok(v) => Ok(v),\n    Err(e) if e.to_string().contains(\"não respondeu depois de 5 tentativas\") => {\n        tokio::time::sleep(Duration::from_secs(60)).await; // cool-down, then retry\n        gql.query(&q).await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Throttle GQL call rate and cache responses to stay under Twitch's rate limits.","Use exponential backoff with jitter in the retry loop.","Avoid datacenter/VPN IPs that are aggressively rate-limited by Twitch.","Monitor for HTTP 429 in the `last` status and pause the pipeline when it appears."],"tags":["network","http","twitch","rate-limit","retry"],"backgroundTag":"request-timeout","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"}