tonhowtf/omniget · error

/

Error message

{} / {}

What it means

unroll first tries the fxtwitter API, then falls back to GraphQL; if BOTH fail it throws `{fx_err} / {graphql_err}`, concatenating the two underlying errors. This is a wrapper error: it means the thread could not be fetched from either backend, and both root causes are preserved in the single message.

Solutions

  1. Read both halves of the message: fix each root cause separately (fxtwitter side vs GraphQL side)
  2. For 429/rate-limit causes, back off and retry after a delay; for deleted/protected tweets, surface a user-facing 'tweet unavailable' instead of retrying
  3. Check connectivity/proxy settings; if GraphQL repeatedly fails due to X API changes, update the omniget-core GraphQL query definitions

Example fix

// before
unroll_graphql(&id)
    .await
    .map_err(|e| anyhow!("{} / {}", fx_err, e))
// after
unroll_graphql(&id).await.map_err(|e| {
    let combined = format!("fxtwitter: {fx_err}; graphql: {e}");
    if fx_err.to_string().contains("404") && e.to_string().contains("404") {
        anyhow!("post do X nao disponivel (removido ou privado)")
    } else {
        anyhow!(combined)
    }
})
Defensive patterns

Strategy: fallback

Validate before calling

// no pre-call validation possible; both backends fail at request time.
// Optionally probe availability first:
// let probe = client.get(format!("https://api.fxtwitter.com/status/{id}")).send().await?;
// if !probe.status().is_success() { warn "post possivelmente indisponivel" }

Type guard

fn is_unavailable_tweet(err: &str) -> bool {
    err.contains("404") || err.to_ascii_lowercase().contains("not found")
}

Try / catch

match unroll(id).await {
    Err(e) => {
        let msg = e.to_string(); // "fxtwitter_err / graphql_err"
        if let Some((fx, gql)) = msg.split_once(" / ") {
            eprintln!("fxtwitter: {fx} | graphql: {gql}");
        }
        if is_unavailable_tweet(&msg) {
            eprintln!("post removido, privado ou inexistente");
        } else {
            schedule_retry_with_backoff(id); // transient: 429/5xx/network
        }
    }
    Ok(t) => t,
}

Prevention

When it happens

Trigger: Calling unroll when fxtwitter returns an error (tweet deleted/NSFW-gated, network failure, fxtwitter outage) AND the GraphQL fallback also fails (guest token rejected, rate limit 429, GraphQL schema/auth change, network error).

Common situations: Deleted or protected tweets (both backends 404); fxtwitter API downtime combined with X's GraphQL guest-token rotation failing; heavy rate limiting after bulk unrolls; Twitter changing GraphQL query IDs causing the fallback to error.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/392ad1bbaf26bd2f. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/x/thread.rs:55

    match super::fx::thread(&id).await {
        Ok((focal, posts, truncated)) => {
            let posts = if posts.is_empty() {
                vec![focal.clone()]
            } else {
                posts
            };
            Ok(Thread {
                focal,
                posts,
                truncated,
                source: "fxtwitter".into(),
            })
        }
        Err(fx_err) => {
            tracing::info!("[x] fxtwitter falhou ({}), tentando GraphQL", fx_err);
            unroll_graphql(&id)
                .await
                .map_err(|e| anyhow!("{} / {}", fx_err, e))
        }
    }
}

async fn unroll_graphql(id: &str) -> anyhow::Result<Thread> {
    let client = XClient::new()?;
    let mut all: Vec<XPost> = Vec::new();
    let mut cursor: Option<String> = None;
    for _ in 0..4 {
        let mut vars = json!({
            "focalTweetId": id,
            "with_rux_injections": false,
            "rankingMode": "Relevance",
            "includePromotedContent": false,
            "withCommunity": true,
            "withQuickPromoteEligibilityTweetFields": true,
            "withBirdwatchNotes": true,
            "withVoice": true,

View on GitHub (pinned to 8600b91f42)