tonhowtf/omniget · error

Age-restricted content

Error message

Age-restricted content

What it means

Raised when the tweet result is a tombstone with reason "NsfwLoggedOut" or its tombstone text starts with "Age-restricted": the media is age-gated and the current (logged-out) session is not allowed to view it. The library deliberately blocks extraction here. It reflects X's sensitive-content policy for unauthenticated clients, not a parsing failure.

Solutions

  1. Use cookies from a logged-in, age-verified X account for sensitive media.
  2. Detect the NsfwLoggedOut reason up front and surface a clear 'age-restricted' message to end users.
  3. If legitimate, route the request through an authenticated extraction path rather than the guest GraphQL one.
  4. Treat this as a terminal condition — do not retry the same logged-out request.

Example fix

// before
if reason == "NsfwLoggedOut" || tombstone_text.starts_with("Age-restricted") {
    return Err(anyhow!("Age-restricted content"));
}
// after
if reason == "NsfwLoggedOut" || tombstone_text.starts_with("Age-restricted") {
    return Err(TwitterError::AgeRestricted { tweet_id });
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect age-gated tombstones before media extraction:
let reason = tweet_result.pointer("/result/reason").or_else(|| tweet_result.get("reason"))
    .and_then(|v| v.as_str()).unwrap_or("");
let tombstone = tweet_result.pointer("/tombstone/text/text").and_then(|v| v.as_str()).unwrap_or("");
if reason == "NsfwLoggedOut" || tombstone.starts_with("Age-restricted") {
    return Err(DisplayError::AgeRestricted);
}

Type guard

fn is_age_restricted(tweet_result: &serde_json::Value) -> bool {
    let reason = tweet_result.pointer("/result/reason")
        .or_else(|| tweet_result.get("reason"))
        .and_then(|v| v.as_str()).unwrap_or("");
    let text = tweet_result.pointer("/tombstone/text/text")
        .and_then(|v| v.as_str()).unwrap_or("");
    reason == "NsfwLoggedOut" || text.starts_with("Age-restricted")
}

Try / catch

match extractor.fetch(tweet_url) {
    Err(e) if e.to_string() == "Age-restricted content" => {
        ui.show("This media is age-restricted; sign in with an age-verified account.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: extract_graphql_media encounters a tombstone whose reason is "NsfwLoggedOut" or whose /tombstone/text/text begins with "Age-restricted" — sensitive-media tweets fetched without an age-verified logged-in session.

Common situations: Scraping sensitive-media tweets with guest tokens only; X tightened logged-out access to NSFW content; region/age verification missing on the credentials used.

Related errors


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

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/twitter.rs:464

                if reason == "Protected" {
                    return Err(anyhow!("Post privado"));
                }

                let tombstone_text = tweet_result
                    .pointer("/tombstone/text/text")
                    .and_then(|v| v.as_str())
                    .unwrap_or("");

                tracing::warn!(
                    "[twitter] graphql tombstone tweet_id={} reason='{}' tombstone_text='{}'",
                    tweet_id,
                    reason,
                    tombstone_text
                );

                if reason == "NsfwLoggedOut" || tombstone_text.starts_with("Age-restricted") {
                    return Err(anyhow!("Age-restricted content"));
                }

                Err(anyhow!("Post not available"))
            }
            "Tweet" | "TweetWithVisibilityResults" => {
                let media = Self::media_arrays_from_tweet_result(tweet_result)
                    .ok_or_else(|| anyhow!("No media found in tweet"))?;
                tracing::debug!(
                    "[twitter] graphql extracted {} media entries for tweet_id={}",
                    media.len(),
                    tweet_id
                );
                Ok(media)
            }
            _ => Err(anyhow!("Post not available")),
        }
    }

View on GitHub (pinned to 8600b91f42)