tonhowtf/omniget · warning
X_RATE_LIMIT
X_RATE_LIMIT
Error message
X_RATE_LIMIT:{} What it means
The X client's response check converts HTTP 429 into the sentinel error 'X_RATE_LIMIT:<seconds>' where <seconds> is derived from the x-rate-limit-reset header (fallback 900s). Callers are expected to parse the reset value and wait that long before retrying the GraphQL operation.
Solutions
- Parse the integer after 'X_RATE_LIMIT:' and schedule a retry after that many seconds.
- Add exponential backoff with jitter and cache results to reduce request volume.
- Spread requests across endpoints/accounts, and respect x-rate-limit-remaining headers proactively.
- Stop polling loops that ignore prior 429s — they extend the lockout.
Example fix
// before
loop { match client.gql_get(op, &q).await { Ok(v) => break v, Err(_) => continue } }
// after
match client.gql_get(op, &q).await {
Ok(v) => v,
Err(e) if e.to_string().starts_with("X_RATE_LIMIT:") => {
let reset: i64 = e.to_string().rsplit(':').next().unwrap().parse()?;
tokio::time::sleep(Duration::from_secs(reset as u64 + 1)).await;
client.gql_get(op, &q).await?
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Try / catch
// parse reset seconds and sleep before retrying
if let Some(reset) = e.to_string().strip_prefix("X_RATE_LIMIT:").and_then(|s| s.parse::<u64>().ok()) {
tokio::time::sleep(Duration::from_secs(reset + 1)).await;
return op().await; // one bounded retry
} Prevention
- Implement global rate limiting/backoff in the X client wrapper, not per-callsite.
- Cache GraphQL results to reduce duplicate requests.
- Watch x-rate-limit-remaining and throttle proactively.
- Never run unbounded retry loops against X endpoints.
When it happens
Trigger: Any gql request whose response carries status 429 — exceeding X's per-endpoint rate limits, especially when polling rapidly or sharing one account/IP across many requests.
Common situations: Tight retry loops hammering one GraphQL endpoint; batch jobs without backoff; multiple app instances behind the same IP consuming the shared limit.
Related errors
- o Trakt está limitando o acesso
- FxTwitter: limite de requisicoes atingido, tente de novo em…
- Server returned error 429 (too many requests). Try again…
- Twitch GQL não respondeu depois de 5 tentativas
- o servidor está limitando o acesso
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/0673fb986da6d321.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/x/client.rs:304
let path = format!("/i/api/graphql/{}/{}", id, op);
if self.authed() {
(format!("https://x.com{}", path), path)
} else {
(format!("https://api.x.com/graphql/{}/{}", id, op), path)
}
}
async fn check(resp: reqwest::Response, op: &str) -> anyhow::Result<Result<Value, String>> {
let status = resp.status();
if status.as_u16() == 429 {
let reset = resp
.headers()
.get("x-rate-limit-reset")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.parse::<i64>().ok())
.map(|r| (r - chrono::Utc::now().timestamp()).max(1))
.unwrap_or(900);
return Err(anyhow!("X_RATE_LIMIT:{}", reset));
}
let text = resp.text().await.unwrap_or_default();
if status.as_u16() == 404 {
return Ok(Err("not_found".into()));
}
if status.as_u16() == 401 || status.as_u16() == 403 {
return Ok(Err(format!("auth:{}", status.as_u16())));
}
if !status.is_success() {
return Err(anyhow!(
"X {}: HTTP {} {}",
op,
status,
text.chars().take(200).collect::<String>()
));
}
let v: Value = serde_json::from_str(&text)
.map_err(|e| anyhow!("X {}: resposta invalida ({})", op, e))?;View on GitHub (pinned to 8600b91f42)