jlcodes99/cockpit-tools · error
Token 刷新失败: {}
Error message
Token 刷新失败: {} What it means
Emitted in import_from_files_logic when modules::oauth::refresh_access_token fails while validating a candidate account found in an imported file. The refresh error `e` is wrapped as "Token 刷新失败: {}" and the entry is added to the failed list (labeled with the entry email or 'unknown'); import of remaining entries continues.
Source
Thrown at crates/cockpit-core/src/modules/import.rs:690
modules::account::update_account_notes(&new_account.id, notes)
{
new_account = acc;
}
}
modules::logger::log_info(&format!("导入账号成功: {}", new_account.email));
imported.push(new_account);
}
Err(e) => {
let msg = format!("保存失败: {}", e);
modules::logger::log_error(&format!("保存账号失败 {}: {}", email, msg));
failed.push(FileImportFailure { email, error: msg });
}
}
}
Err(e) => {
let label = entry.email.as_deref().unwrap_or("unknown").to_string();
let msg = format!("Token 刷新失败: {}", e);
modules::logger::log_error(&format!("{}: {}", label, msg));
failed.push(FileImportFailure {
email: label,
error: msg,
});
}
}
}
modules::logger::log_info(&format!(
"文件导入完成,成功 {} 个,失败 {} 个",
imported.len(),
failed.len()
));
if !imported.is_empty() {
modules::websocket::broadcast_data_changed("import_from_files");
}
View on GitHub (pinned to 1ed8b77992)
Solutions
- Check the wrapped cause: if it is an HTTP 400/invalid_grant, the token is dead — the account must be re-logged-in rather than imported.
- Verify the source file's refresh_token values are current and unrotated; export a fresh dump from the working tool.
- Confirm network access to the OAuth token endpoint and retry the import.
- Inspect result.failed after import; re-export/re-import only the failed emails after re-authenticating them.
- Ensure the file's accounts belong to the same provider/client this build expects.
Example fix
// before: assuming all entries import
let result = import_from_files_logic(&paths).await?;
// after: handle stale refresh tokens explicitly
let result = import_from_files_logic(&paths).await?;
for f in &result.failed {
if f.error.contains("Token 刷新失败") {
eprintln!("{}: refresh token rejected, needs re-login", f.email);
}
} Defensive patterns
Strategy: validation
Validate before calling
// Validate candidate refresh tokens before attempting refresh
fn candidate_token_valid(entry: &ImportEntry) -> bool {
let t = entry.refresh_token.trim();
!t.is_empty() && t.len() >= 20 && t.chars().all(|c| !c.is_control())
} Type guard
fn is_refresh_token_error(failure: &FileImportFailure) -> bool {
failure.error.starts_with("Token 刷新失败: ")
} Try / catch
let result = import_from_files_logic(&paths).await?;
let stale: Vec<_> = result.failed.iter().filter(|f| is_refresh_token_error(f)).collect();
for f in stale {
eprintln!("{}: refresh token rejected — re-authenticate this account", f.email);
} Prevention
- Export account files from a live, recently-authenticated tool — old dumps often hold rotated tokens.
- Validate JSON structure and token length before importing.
- Keep source files untouched (no manual edits/truncation) between export and import.
- Ensure network access to the OAuth endpoint during import.
- Review the failed list in FileImportResult after each import.
When it happens
Trigger: File import where a candidate refresh_token is invalid, expired, or revoked so the OAuth token endpoint rejects it; network failure during the token request; malformed refresh_token extracted from the file.
Common situations: Importing old account dumps whose refresh tokens were rotated or revoked; hand-edited or truncated JSON files; tokens from a different environment/client_id; offline machine or blocked network during import.
Related errors
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/a7865bf28704fc12.
Report an issue: GitHub.