tonhowtf/omniget · error
Post privado
Error message
Post privado
What it means
Raised when the tweet result's reason field equals "Protected": the target account is private/protected, so its posts are only visible to approved followers. The library is (typically) making a logged-out request and intentionally refuses with the Spanish-language message 'Post privado' instead of attempting extraction. This is an expected, deliberate error for protected content.
Solutions
- Inform the user the post belongs to a protected account and cannot be fetched without follower access.
- Use authenticated cookies belonging to an account that follows the protected user.
- Check the account's visibility (protected flag) before attempting extraction to fail fast with a clearer message.
- Map this reason to a typed error so callers can distinguish it from generic unavailability.
Example fix
// before
if reason == "Protected" {
return Err(anyhow!("Post privado"));
}
// after
if reason == "Protected" {
return Err(TwitterError::ProtectedPost { tweet_id });
} Defensive patterns
Strategy: validation
Validate before calling
// Before extraction, check account visibility via the user API:
let is_protected = user.get("protected").and_then(|v| v.as_bool()).unwrap_or(false);
if is_protected {
return Err(DisplayError::ProtectedPost);
} Type guard
fn is_protected_reason(tweet_result: &serde_json::Value) -> bool {
tweet_result.pointer("/result/reason").or_else(|| tweet_result.get("reason"))
.and_then(|v| v.as_str()) == Some("Protected")
} Try / catch
match extractor.fetch(tweet_url) {
Err(e) if e.to_string() == "Post privado" => {
ui.show("This post is from a protected account and cannot be downloaded.");
}
other => other?,
} Prevention
- Resolve the author's screen name first and check the protected flag before media extraction.
- Treat this error as terminal — never retry without follower credentials.
- Surface a user-friendly 'private account' message rather than the raw error.
- Detect the 'Protected' reason string in your own response parsing to pre-empt it.
When it happens
Trigger: extract_graphql_media resolves a tweet result whose __typename is TweetTombstone (or has /result/reason) and reason == "Protected" — i.e. the tweet belongs to a protected X account.
Common situations: User pasted a link to a private account's tweet; the account was switched to protected after the link was shared; scraper runs without follower credentials, so any protected tweet fails.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- chave de verificacao do X nao encontrada
- chunk de assinatura do X nao encontrado
- frame da animacao fora do intervalo
- Age-restricted content
- No media found in tweet
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/411bc0287c9ab1c9.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/platforms/twitter.rs:448
.get("__typename")
.and_then(|v| v.as_str())
.unwrap_or("");
tracing::debug!(
"[twitter] graphql media typename={} tweet_id={}",
typename,
tweet_id
);
match typename {
"TweetUnavailable" | "TweetTombstone" => {
let reason = tweet_result
.pointer("/result/reason")
.or_else(|| tweet_result.get("reason"))
.and_then(|v| v.as_str())
.unwrap_or("");
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"));
}
View on GitHub (pinned to 8600b91f42)