farion1231/cc-switch · error · anyhow::Error
Skill backup is not a directory: {}
Error message
Skill backup is not a directory: {} What it means
delete_backup resolves the backup id to a path under the backup directory (rejecting traversal) and stats it with symlink_metadata to avoid following links. If the entry exists but is not a directory (regular file, symlink, or other), it refuses remove_dir_all semantics and aborts. This is a safety guard against deleting through unexpected filesystem entries.
Source
Thrown at src-tauri/src/services/skill.rs:1745
skill: metadata.skill,
}),
Err(err) => {
log::warn!("解析 Skill 备份失败 {}: {err:#}", path.display());
}
}
}
entries.sort_by_key(|entry| std::cmp::Reverse(entry.created_at));
Ok(entries)
}
pub fn delete_backup(backup_id: &str) -> Result<()> {
let backup_path = Self::backup_path_for_id(backup_id)?;
let metadata = fs::symlink_metadata(&backup_path)
.with_context(|| format!("failed to access {}", backup_path.display()))?;
if !metadata.is_dir() {
return Err(anyhow!(
"Skill backup is not a directory: {}",
backup_path.display()
));
}
fs::remove_dir_all(&backup_path)
.with_context(|| format!("failed to delete {}", backup_path.display()))?;
log::info!("Skill 备份已删除: {}", backup_path.display());
Ok(())
}
pub fn restore_from_backup(
db: &Arc<Database>,
backup_id: &str,
current_app: &AppType,
) -> Result<InstalledSkill> {
let _state_guard = skill_state_write_guard();View on GitHub (pinned to 0b5da51016)
Solutions
- Inspect the exact path printed in the error message under the skill backups directory
- If it is a stray file/symlink, remove it manually with the file manager or rm
- Check whether a cloud-sync client is managing the backups folder and exclude it
Defensive patterns
Strategy: validation
Validate before calling
const backups = await invoke<Array<{ id: string; created_at: number }>>('list_skill_backups');
const target = backups.find(b => b.id === backupId);
if (!target) { notify('Backup no longer listed — refresh'); return; } Try / catch
try {
await invoke('delete_skill_backup', { backupId });
} catch (e) {
const msg = String(e);
if (msg.includes('not a directory')) {
notify(`Remove the stray entry manually: ${msg}`); // path is in the message
return;
}
throw e;
} Prevention
- Exclude the backups directory from cloud-sync clients
- Do not hand-edit the backups folder structure
- Treat 'not a directory' as filesystem tampering — inspect before deleting anything manually
When it happens
Trigger: A backup 'directory' replaced by a regular file or symlink (manual tampering, partial backup creation, copy tools that flatten structure); a previous backup write interrupted at directory-creation time.
Common situations: Users hand-editing the backups folder; backup tools or sync clients (Dropbox/OneDrive) replacing directories with placeholder files or links; interrupted backup writes after disk-full.
Related errors
- Skill backup is invalid or missing SKILL.md: {}
- Skill already exists, please uninstall the current one first
- Restore target already exists: {}
- OPENCLAW_ENV_EMPTY
- OPENCLAW_ENV_INVALID_JSON
AI-assisted analysis of farion1231/cc-switch@0b5da51016 (2026-08-20).
Data as JSON: /api/errors/bffcaf5608363640.
Report an issue: GitHub.