{"record":{"id":"c19bf1a93a022b27","repo":"tonhowtf/omniget","slug":"o-trakt-n-o-devolveu-o-token","errorCode":null,"errorMessage":"o Trakt não devolveu o token","messagePattern":"o Trakt não devolveu o token","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/lists/trakt.rs","lineNumber":354,"sourceCode":"                    username: username.clone(),\n                    message: format!(\"conectado como {}\", username),\n                });\n            }\n            Poll::SlowDown => interval += Duration::from_secs(dc.interval.max(1)),\n            Poll::Pending => {}\n            other => return Err(anyhow!(\"{}\", other.message())),\n        }\n    }\n}\n\nfn save_token(v: &Value) -> Result<String> {\n    let access = v\n        .get(\"access_token\")\n        .and_then(|x| x.as_str())\n        .unwrap_or_default()\n        .to_string();\n    if access.is_empty() {\n        return Err(anyhow!(\"o Trakt não devolveu o token\"));\n    }\n    let refresh = v\n        .get(\"refresh_token\")\n        .and_then(|x| x.as_str())\n        .unwrap_or_default()\n        .to_string();\n    let created = v.get(\"created_at\").and_then(|x| x.as_i64()).unwrap_or(0);\n    let expires = v.get(\"expires_in\").and_then(|x| x.as_i64()).unwrap_or(0);\n    let base = if created > 0 {\n        created\n    } else {\n        chrono::Utc::now().timestamp()\n    };\n    let _g = LOCK.lock().unwrap_or_else(|e| e.into_inner());\n    let mut c = load();\n    c.access_token = access;\n    c.refresh_token = refresh;\n    c.expires_at = base + expires;","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/lists/trakt.rs#L336-L372","documentation":"Thrown by save_token when the token JSON returned by Trakt has an empty or missing access_token. The token cannot be persisted or used, so the connection attempt fails. Called from wait_for_token's poll success and refresh_if_needed.","triggerScenarios":"Poll success or refresh response body lacks a non-empty access_token string — e.g. Trakt returned an error-shaped JSON, or the response was truncated/mangled by a proxy.","commonSituations":"Proxies/VPN returning 200 with an HTML or error body; Trakt API behavioral changes; tests/mocks with incomplete token payloads; a refresh call that actually failed but returned 200.","solutions":["Log the full token response body to see what Trakt actually returned","Check for proxy/VPN interference on api.trakt.tv","Restart the device flow (device_code + wait_for_token) to obtain a fresh token","If the response shape changed, update the field extraction in save_token"],"exampleFix":"// before\nlet v: Value = r.json().await?;\nsave_token(&v)?;\n// after\nlet v: Value = r.json().await?;\ntracing::debug!(\"token keys: {:?}\", v.as_object().map(|o| o.keys().collect::<Vec<_>>()));\nsave_token(&v)?;","handlingStrategy":"try-catch","validationCode":"if body.get(\"access_token\").and_then(|x| x.as_str()).map_or(true, str::is_empty) {\n    // do not call save_token; log body and surface a reconnect error\n}","typeGuard":"fn has_access_token(v: &serde_json::Value) -> bool {\n    v.get(\"access_token\").and_then(|x| x.as_str()).map_or(false, |s| !s.is_empty())\n}","tryCatchPattern":"match connect_flow().await {\n    Err(e) if e.to_string().contains(\"não devolveu o token\") => {\n        // inspect raw response, check proxy/VPN, restart device flow\n    }\n    other => other?,\n}","preventionTips":["Validate token payload fields before persisting","Disable interfering proxies/VPNs for api.trakt.tv","Persist tokens atomically right after receipt"],"tags":["oauth","trakt","token","json"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}