{"record":{"id":"411bc0287c9ab1c9","repo":"tonhowtf/omniget","slug":"post-privado","errorCode":null,"errorMessage":"Post privado","messagePattern":"Post privado","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/twitter.rs","lineNumber":448,"sourceCode":"            .get(\"__typename\")\n            .and_then(|v| v.as_str())\n            .unwrap_or(\"\");\n        tracing::debug!(\n            \"[twitter] graphql media typename={} tweet_id={}\",\n            typename,\n            tweet_id\n        );\n\n        match typename {\n            \"TweetUnavailable\" | \"TweetTombstone\" => {\n                let reason = tweet_result\n                    .pointer(\"/result/reason\")\n                    .or_else(|| tweet_result.get(\"reason\"))\n                    .and_then(|v| v.as_str())\n                    .unwrap_or(\"\");\n\n                if reason == \"Protected\" {\n                    return Err(anyhow!(\"Post privado\"));\n                }\n\n                let tombstone_text = tweet_result\n                    .pointer(\"/tombstone/text/text\")\n                    .and_then(|v| v.as_str())\n                    .unwrap_or(\"\");\n\n                tracing::warn!(\n                    \"[twitter] graphql tombstone tweet_id={} reason='{}' tombstone_text='{}'\",\n                    tweet_id,\n                    reason,\n                    tombstone_text\n                );\n\n                if reason == \"NsfwLoggedOut\" || tombstone_text.starts_with(\"Age-restricted\") {\n                    return Err(anyhow!(\"Age-restricted content\"));\n                }\n","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/twitter.rs#L430-L466","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif reason == \"Protected\" {\n    return Err(anyhow!(\"Post privado\"));\n}\n// after\nif reason == \"Protected\" {\n    return Err(TwitterError::ProtectedPost { tweet_id });\n}","handlingStrategy":"validation","validationCode":"// Before extraction, check account visibility via the user API:\nlet is_protected = user.get(\"protected\").and_then(|v| v.as_bool()).unwrap_or(false);\nif is_protected {\n    return Err(DisplayError::ProtectedPost);\n}","typeGuard":"fn is_protected_reason(tweet_result: &serde_json::Value) -> bool {\n    tweet_result.pointer(\"/result/reason\").or_else(|| tweet_result.get(\"reason\"))\n        .and_then(|v| v.as_str()) == Some(\"Protected\")\n}","tryCatchPattern":"match extractor.fetch(tweet_url) {\n    Err(e) if e.to_string() == \"Post privado\" => {\n        ui.show(\"This post is from a protected account and cannot be downloaded.\");\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["twitter","protected-content","authorization","scraping"],"backgroundTag":"permission-denied","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}