{"record":{"id":"828bfe0b3e10f6e1","repo":"tonhowtf/omniget","slug":"token-expired-828bfe","errorCode":"token_expired","errorMessage":"token_expired","messagePattern":"token_expired","errorType":"error_code","errorClass":"anyhow::Error","httpStatus":403,"severity":"error","filePath":"src-tauri/src/platforms/twitter/mod.rs","lineNumber":364,"sourceCode":"            .header(\"Accept-Language\", \"en\")\n            .header(\"Content-Type\", \"application/json\")\n            .header(\"Cookie\", &cookie_val);\n        if has_auth_token {\n            request = request.header(\"x-twitter-auth-type\", \"OAuth2Session\");\n        }\n        if let Some(ct0) = ct0 {\n            request = request.header(\"x-csrf-token\", ct0);\n        }\n\n        let response = request.send().await?;\n\n        let status = response.status();\n        tracing::debug!(\"[twitter] graphql tweet_id={} status={}\", tweet_id, status);\n\n        if status == reqwest::StatusCode::FORBIDDEN\n            || status == reqwest::StatusCode::TOO_MANY_REQUESTS\n        {\n            return Err(anyhow!(\"token_expired\"));\n        }\n\n        if status == reqwest::StatusCode::NOT_FOUND {\n            return Err(anyhow!(\"Post not available\"));\n        }\n\n        if !status.is_success() {\n            return Err(anyhow!(\"Twitter API retornou HTTP {}\", status));\n        }\n\n        response.json().await.map_err(Into::into)\n    }\n\n    fn calculate_syndication_token(id: &str) -> String {\n        let num: f64 = id.parse().unwrap_or(0.0);\n        let raw = (num / 1e15) * std::f64::consts::PI;\n        let base36 = Self::f64_to_base36(raw);\n        base36","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/platforms/twitter/mod.rs#L346-L382","documentation":"request_tweet maps HTTP 403 (Forbidden) and 429 (Too Many Requests) from Twitter's GraphQL endpoint to the error code `token_expired`. The guest token is treated as stale/rejected whenever Twitter refuses the request with these statuses, signaling the caller to refresh the token and retry.","triggerScenarios":"GET to the TweetResultByRestId GraphQL endpoint returns 403 (guest token rejected/expired or auth challenge) or 429 (rate limited). Both are collapsed into the same `token_expired` error even though 429 is actually rate limiting.","commonSituations":"Guest token cached for too long and Twitter invalidated it; bursts of downloads hitting Twitter's per-token rate limit (429); Twitter tightening bot protection so 403 is returned even for fresh tokens.","solutions":["Call get_guest_token to obtain and cache a fresh guest token, then retry request_tweet","Distinguish 429 from 403 and add exponential backoff with Retry-After for 429 instead of only refreshing the token","Reduce request frequency / add throttling and caching of tweet lookups","Consider authenticated (OAuth) access if guest-token requests keep getting 403"],"exampleFix":"// before\nif status == reqwest::StatusCode::FORBIDDEN || status == reqwest::StatusCode::TOO_MANY_REQUESTS {\n    return Err(anyhow!(\"token_expired\"));\n}\n// after\nmatch status {\n    reqwest::StatusCode::TOO_MANY_REQUESTS => Err(anyhow!(\"rate_limited\")),\n    reqwest::StatusCode::FORBIDDEN => Err(anyhow!(\"token_expired\")),\n    _ => Ok(())\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match request_tweet(id).await {\n    Err(e) if e.to_string() == \"token_expired\" => {\n        platform.refresh_guest_token().await?;\n        request_tweet(id).await\n    }\n    other => other,\n}","preventionTips":["Refresh the guest token periodically instead of caching indefinitely","Throttle tweet requests to stay under Twitter's rate limits","Retry once with a fresh token before surfacing the error","Treat repeated 403s as a signal Twitter changed guest-auth requirements"],"tags":["http","twitter","guest-token","rate-limit","forbidden"],"backgroundTag":"jwt-token-expired","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"}