tonhowtf/omniget · error

X

Error message

X {}: {}

What it means

The X (Twitter) GraphQL endpoint returned HTTP 200 with an empty/null `data` field and an `errors` array whose first message is not a known recoverable case (not "Query not found"/query_id, not 404/401/403 already handled). The client surfaces the raw GraphQL error message prefixed with the operation name, e.g. `X UserByScreenName: Field ... is required`.

Solutions

  1. Read the operation name and message in the error, then fix the variables/features payload for that op to match X's current schema
  2. Refresh query ids and features (clear cached query_ids so a fresh manifest is fetched)
  3. Verify the target resource exists and is accessible with the current session (some ops fail for private/suspended accounts)
  4. Log the full response body (check truncates it); reproduce with curl using the same cookies/csrf to confirm the schema mismatch
  5. Update omniget-core's per-op feature flags to the current values scraped from x.com

Example fix

// before
client.gql_get("TweetDetail", json!({"id": id}), json!({}), None)?;
// after
client.gql_get("TweetDetail", json!({"focalTweetId": id, "withReactionsMetadata": false, "withReactionsPerspective": false}), json!({"article_pivot_enabled": true}), None)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: validate variables before the call
if tweet_id.is_empty() || !tweet_id.chars().all(|c| c.is_ascii_digit()) {
    return Err(anyhow!("invalid tweet id"));
}

Try / catch

match client.gql_get(op, vars, feats, None).await {
    Err(e) if e.to_string().starts_with("X ") => {
        tracing::warn!("GraphQL rejected {}: {} — refresh ids/features and retry once", op, e);
        client.refresh_ids().await?;
        client.gql_get(op, vars, feats, None).await
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling gql_get/gql_post/rest_post_form when X rejects the request at the GraphQL layer: missing or invalid required variables, unsupported/disabled feature flags, invalid fieldToggles, or a suspended/private target returning errors in a 200 body.

Common situations: X changed the GraphQL schema (renamed/removed a field or feature flag), the caller passes malformed variables (wrong tweet/user id format), or an operation's hardcoded features map is stale after a X rollout.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/x/client.rs:337

            ));
        }
        let v: Value = serde_json::from_str(&text)
            .map_err(|e| anyhow!("X {}: resposta invalida ({})", op, e))?;
        if v.get("data")
            .map(|d| d.is_null() || d.as_object().map(|o| o.is_empty()).unwrap_or(false))
            .unwrap_or(true)
        {
            if let Some(msg) = v
                .get("errors")
                .and_then(|e| e.as_array())
                .and_then(|a| a.first())
                .and_then(|e| e.get("message"))
                .and_then(|m| m.as_str())
            {
                if msg.contains("Query not found") || msg.contains("query_id") {
                    return Ok(Err("not_found".into()));
                }
                return Err(anyhow!("X {}: {}", op, msg));
            }
        }
        Ok(Ok(v))
    }

    async fn refresh_ids(&self) -> anyhow::Result<()> {
        super::query_ids::refresh(&self.http, self.cookie.as_deref())
            .await
            .map(|_| ())
    }

    pub async fn gql_get(
        &self,
        op: &str,
        variables: Value,
        extra_features: Value,
        field_toggles: Option<Value>,
    ) -> anyhow::Result<Value> {

View on GitHub (pinned to 8600b91f42)