farion1231/cc-switch · error · anyhow::Error
Restore target already exists: {}
Error message
Restore target already exists: {} What it means
The database has no conflicting skill (id or directory), but the target path under the SSOT directory already exists on disk — as a file, directory, or symlink. Restore never overwrites existing filesystem state, even orphaned state no DB row references.
Source
Thrown at src-tauri/src/services/skill.rs:1795
skill
.directory
.eq_ignore_ascii_case(&metadata.skill.directory)
})
{
return Err(anyhow!(
"Skill already exists, please uninstall the current one first: {}",
metadata.skill.directory
));
}
// meta.json 是文件内容(可能来自手工放置或不可信备份),directory 此前
// 未经任何校验就直接 join——可穿越出 SSOT 目录写任意位置。必须先校验。
let directory = Self::require_valid_directory(&metadata.skill.directory)?;
let ssot_dir = Self::get_ssot_dir()?;
let restore_path = ssot_dir.join(&directory);
if restore_path.exists() || Self::is_symlink(&restore_path) {
return Err(anyhow!(
"Restore target already exists: {}",
restore_path.display()
));
}
let mut restored_skill = metadata.skill;
restored_skill.directory = directory;
restored_skill.installed_at = Utc::now().timestamp();
restored_skill.apps = SkillApps::only(current_app);
restored_skill.updated_at = 0;
Self::copy_dir_recursive(&backup_skill_dir, &restore_path)?;
// 重新计算内容哈希
restored_skill.content_hash = Self::compute_dir_hash(&restore_path).ok();
if let Err(err) = db.save_skill(&restored_skill) {
let _ = fs::remove_dir_all(&restore_path);View on GitHub (pinned to 0b5da51016)
Solutions
- Inspect the exact path in the error message under the SSOT skills directory
- If it is a leftover you do not need, move it aside or delete it manually, then retry the restore
- If unsure of its origin, rename it (e.g. add .bak) instead of deleting, then restore
Defensive patterns
Strategy: try-catch
Try / catch
try {
await invoke('restore_skill_backup', { backupId, currentApp });
} catch (e) {
const msg = String(e);
if (msg.includes('Restore target already exists')) {
notify(`Leftover files at the target — inspect and move aside: ${msg}`);
return;
}
throw e;
} Prevention
- After failed uninstalls, check for leftover directories in the SSOT skills folder
- Do not manually place folders into the SSOT directory
- Rename leftovers to *.bak rather than deleting when origin is unclear
When it happens
Trigger: Leftover directory from an uninstall whose DB delete succeeded but file removal failed or was skipped; a manual copy of the skill into the SSOT folder; a symlink planted at the target path.
Common situations: Earlier failed/partial uninstalls; users hand-managing the SSOT directory; sync tools recreating folders.
Related errors
- Skill backup is not a directory: {}
- Skill backup is invalid or missing SKILL.md: {}
- Skill already exists, please uninstall the current one first
- Skill 存储目录不能与 {app:?} 的 Skills 目录相同: {}
- Skill directory changed during install; please retry
AI-assisted analysis of farion1231/cc-switch@0b5da51016 (2026-08-20).
Data as JSON: /api/errors/78c594122a0fc999.
Report an issue: GitHub.