jlcodes99/cockpit-tools · error
保存失败: {}
Error message
保存失败: {} What it means
Produced in import_from_files_logic after a refresh_token from an imported file was successfully exchanged, but upsert_account failed to persist the resulting account. The underlying storage error `e` is wrapped as "保存失败: {}" and recorded per-email in the FileImportFailure list; the import continues with other entries.
Source
Thrown at crates/cockpit-core/src/modules/import.rs:682
if let Ok(acc) =
modules::account::update_account_tags(&new_account.id, entry.tags)
{
new_account = acc;
}
}
if let Some(notes) = entry.notes {
if let Ok(acc) =
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!(
"文件导入完成,成功 {} 个,失败 {} 个",View on GitHub (pinned to 1ed8b77992)
Solutions
- Read the inner error after '保存失败: ' to identify the storage-layer cause (I/O, serialization, or constraint).
- Check disk space and that the account storage files/database are writable and not locked by another Cockpit instance.
- Retry the import; failed entries are reported individually so successfully imported accounts are not duplicated.
- If a duplicate email is the cause, delete or update the existing account first, then re-import.
- Back up and repair/recreate a corrupted account store if serialization errors persist.
Example fix
// before: ignoring per-entry failures
let result = import_from_files_logic(paths).await?;
// after: inspect and retry only failed entries
let result = import_from_files_logic(paths).await?;
for f in &result.failed {
eprintln!("import failed for {}: {}", f.email, f.error);
}
if !result.failed.is_empty() {
free_disk_or_unlock_store();
retry_failed_imports(&result.failed)?;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight storage checks before importing
fn storage_ready() -> Result<(), String> {
let dir = account_store_dir();
if !dir.exists() { std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?; }
let probe = dir.join(".write_probe");
std::fs::write(&probe, b"ok").map_err(|e| format!("store not writable: {}", e))?;
std::fs::remove_file(&probe).map_err(|e| e.to_string())
} Type guard
fn is_duplicate_account_error(err: &str) -> bool {
err.contains("duplicate") || err.contains("已存在") || err.contains("unique")
} Try / catch
let result = import_from_files_logic(&paths).await?; // per-entry failures land in result.failed
for f in &result.failed {
if let Some(storage_err) = f.error.strip_prefix("保存失败: ") {
log::error!("save failed for {}: {}", f.email, storage_err);
}
}
if !result.failed.is_empty() { offer_retry_of_failed(&result.failed); } Prevention
- Verify disk space and store writability before starting a bulk import.
- Run only one import at a time to avoid store lock contention.
- Check result.failed after every import instead of assuming full success.
- Back up the account store before large imports.
- Resolve duplicate emails before re-importing the same file.
When it happens
Trigger: Calling import_from_files_logic (file import) where modules::upsert_account returns Err — e.g. storage/serialization failure, disk I/O error, account store lock contention, or an account constraint violation for the resolved email.
Common situations: Accounts file locked or read-only on disk; corrupted local account database; duplicate/invalid email key that the store rejects; running out of disk space; concurrent import sessions racing on the same account record.
Related errors
- invalidJsonMessage
- messages.rawLineNoRefreshToken(item.lineNumber)
- messages.rawLineMultipleRefreshTokens(item.lineNumber)
- messages.invalidJson
- readBundleMessage(msg) ?? readBundleMessage(message) ?? read
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/4dc8a0c7169a7124.
Report an issue: GitHub.