{"record":{"id":"b707bc6802b88fcf","repo":"jlcodes99/cockpit-tools","slug":"refresh-token-1","errorCode":null,"errorMessage":"未获取到 Refresh Token。\n\n可能原因：您之前已授权过此应用\n\n解决方案：\n1. 访问 https://myaccount.google.com/permissions\n2. 撤销 'Antigravity Tools' 的访问权限\n3. 重新进行 OAuth 授权","messagePattern":"未获取到 Refresh Token。\n\n可能原因：您之前已授权过此应用\n\n解决方案：\n1\\. 访问 https://myaccount\\.google\\.com/permissions\n2\\. 撤销 'Antigravity Tools' 的访问权限\n3\\. 重新进行 OAuth 授权","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/commands/oauth.rs","lineNumber":69,"sourceCode":"\n    let token_res = modules::oauth_server::start_oauth_flow(app_handle.clone())\n        .await\n        .map_err(|e| {\n            modules::logger::log_error(&format!(\"OAuth 流程失败: {}\", e));\n            e\n        })?;\n\n    modules::logger::log_info(\"OAuth 授权成功，检查 refresh_token...\");\n\n    let refresh_token = token_res.refresh_token.ok_or_else(|| {\n        let msg = \"未获取到 Refresh Token。\\n\\n\\\n         可能原因：您之前已授权过此应用\\n\\n\\\n         解决方案：\\n\\\n         1. 访问 https://myaccount.google.com/permissions\\n\\\n         2. 撤销 'Antigravity Tools' 的访问权限\\n\\\n         3. 重新进行 OAuth 授权\"\n            .to_string();\n        modules::logger::log_error(&msg);\n        msg\n    })?;\n\n    modules::logger::log_info(\"获取用户信息...\");\n    let user_info = modules::oauth::get_user_info(&token_res.access_token)\n        .await\n        .map_err(|e| {\n            modules::logger::log_error(&format!(\"获取用户信息失败: {}\", e));\n            e\n        })?;\n\n    modules::logger::log_info(&format!(\n        \"用户: {} ({})\",\n        user_info.email,\n        user_info.name.as_deref().unwrap_or(\"无名称\")\n    ));\n\n    let token_data = models::TokenData::new(","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src-tauri/src/commands/oauth.rs#L51-L87","documentation":"Google does not return a refresh_token when the user has already previously granted consent to the OAuth app (unless prompt=consent / access_type=offline is forced). start_oauth_login requires a refresh_token for long-lived access, so when token_res.refresh_token is None it logs and returns this user-facing multi-line error explaining the cause and remedy.","triggerScenarios":"token_res.refresh_token.ok_or_else(...) fires — i.e. the token exchange succeeded but Google omitted refresh_token because this Google account had previously authorized 'Antigravity Tools' and consent was not re-prompted.","commonSituations":"Re-adding an account that was authorized before; the app's OAuth request not forcing prompt=consent&access_type=offline; testing repeatedly with the same Google account; tokens revoked server-side but Google still skipping refresh_token issuance on silent re-consent.","solutions":["Follow the message: visit https://myaccount.google.com/permissions, revoke 'Antigravity Tools' access, then redo the OAuth authorization to force a new refresh_token","Fix the client so the authorization request includes access_type=offline and prompt=consent, guaranteeing a refresh_token on every flow","Use a different Google account for a fresh consent if revocation is not feasible","If the app supports it, use an existing stored account instead of re-running OAuth for the same Google account"],"exampleFix":"// before\nlet refresh_token = token_res.refresh_token.ok_or_else(|| {\n    let msg = \"未获取到 Refresh Token...\".to_string();\n    modules::logger::log_error(&msg);\n    msg\n})?;\n// after\n// client-side: force Google to re-issue a refresh_token every time\nlet auth_url = build_google_auth_url(&client_id, &redirect_uri,\n    \"access_type=offline&prompt=consent\");\n// ...\nlet refresh_token = token_res.refresh_token.ok_or_else(|| {\n    let msg = \"未获取到 Refresh Token...\".to_string();\n    modules::logger::log_error(&msg);\n    msg\n})?;","handlingStrategy":"try-catch","validationCode":"// force refresh_token issuance on every authorization request\nlet auth_url = format!(\n    \"https://accounts.google.com/o/oauth2/v2/auth?client_id={}&redirect_uri={}&response_type=code&scope={}&access_type=offline&prompt=consent\",\n    client_id, urlencode(redirect_uri), urlencode(scopes)\n);","typeGuard":null,"tryCatchPattern":"let refresh_token = match token_res.refresh_token {\n    Some(rt) => rt,\n    None => {\n        // recovery path: instruct user to revoke and re-consent, then retry automatically\n        return Err(\"未获取到 Refresh Token：请访问 https://myaccount.google.com/permissions 撤销应用授权后重试\".to_string());\n    }\n};","preventionTips":["Always request access_type=offline and prompt=consent in the Google auth URL","Revoke the app at myaccount.google.com/permissions before re-adding an existing account","Handle refresh_token absence gracefully instead of assuming Google always returns it","If a stored account exists for the email, reuse it instead of re-running OAuth"],"tags":["oauth","google","refresh-token","consent"],"backgroundTag":"missing-refresh-token","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}