farion1231/cc-switch · error · anyhow::Error

Skill backup is invalid or missing SKILL.md: {}

Error message

Skill backup is invalid or missing SKILL.md: {}

What it means

restore_from_backup requires backup_path/skill/SKILL.md to exist; SKILL.md is the defining artifact of a skill. Its absence means the backup is incomplete, corrupted, or was assembled by hand without the payload directory. meta.json parsed fine, but there is nothing restorable.

Source

Thrown at src-tauri/src/services/skill.rs:1768

        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();
        let backup_path = Self::backup_path_for_id(backup_id)?;
        let metadata = Self::read_backup_metadata(&backup_path)?;
        let backup_skill_dir = backup_path.join("skill");
        if !backup_skill_dir.join("SKILL.md").exists() {
            return Err(anyhow!(
                "Skill backup is invalid or missing SKILL.md: {}",
                backup_path.display()
            ));
        }

        let existing_skills = db.get_all_installed_skills()?;
        if existing_skills.contains_key(&metadata.skill.id)
            || existing_skills.values().any(|skill| {
                skill
                    .directory
                    .eq_ignore_ascii_case(&metadata.skill.directory)
            })
        {
            return Err(anyhow!(
                "Skill already exists, please uninstall the current one first: {}",
                metadata.skill.directory
            ));
        }

View on GitHub (pinned to 0b5da51016)

Solutions

  1. Open the backup folder shown in the error and verify the layout: meta.json plus skill/SKILL.md
  2. If skill/ is missing or gutted, use a different backup or accept the skill is unrecoverable from this backup
  3. Delete the broken backup entry (delete_skill_backup) to clean the list, and re-create backups from live installs before uninstalling in the future
Defensive patterns

Strategy: validation

Validate before calling

const backups = await invoke<Array<{ id: string }>>('list_skill_backups');
// Prefer offering restore only for backups you did not hand-modify;
// verify the entry still exists and is recent before invoking restore.
if (!backups.some(b => b.id === backupId)) {
  notify('Backup missing — refresh the backup list');
  return;
}

Try / catch

try {
  await invoke('restore_skill_backup', { backupId, currentApp });
} catch (e) {
  if (String(e).includes('missing SKILL.md')) {
    notify('Backup is incomplete — use another backup or delete this entry');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Backup creation interrupted (app killed, disk full) after meta.json was written but before the skill files were copied; files inside the backup deleted afterwards; a backup folder recreated manually with only meta.json; antivirus quarantining SKILL.md.

Common situations: Crash during uninstall-backup; users pruning 'large files' inside backups; backups copied between machines with partial transfer.

Related errors


AI-assisted analysis of farion1231/cc-switch@0b5da51016 (2026-08-20). Data as JSON: /api/errors/714be049aa16eca8. Report an issue: GitHub.