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

Skill 源目录缺少 SKILL.md,拒绝同步以避免覆盖目标目录: {}

Error message

Skill 源目录缺少 SKILL.md,拒绝同步以避免覆盖目标目录: {}

What it means

Thrown by validate_sync_source_dir when the SSOT source directory exists but contains no SKILL.md manifest. The check is deliberate: copying an empty or half-written source over the app destination would silently wipe a working skill, so sync is refused until the source is a valid skill directory again.

Source

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

            fs::remove_dir(path)?; // Windows 的目录 symlink 需要用 remove_dir
        } else if path.is_dir() {
            // 真实目录:递归删除
            fs::remove_dir_all(path)?;
        } else if path.exists() {
            // 普通文件
            fs::remove_file(path)?;
        }
        Ok(())
    }

    fn validate_sync_source_dir(source: &Path, directory: &str) -> Result<()> {
        if !source.is_dir() {
            return Err(anyhow!("Skill 不存在于 SSOT: {directory}"));
        }

        let manifest = source.join("SKILL.md");
        if !manifest.is_file() {
            return Err(anyhow!(
                "Skill 源目录缺少 SKILL.md,拒绝同步以避免覆盖目标目录: {}",
                source.display()
            ));
        }

        Ok(())
    }

    fn replace_dest_with_copy(source: &Path, dest: &Path, directory: &str) -> Result<()> {
        Self::validate_sync_source_dir(source, directory)?;

        let parent = dest
            .parent()
            .ok_or_else(|| anyhow!("Invalid skill destination: {}", dest.display()))?;
        fs::create_dir_all(parent)?;

        let nonce = SystemTime::now()
            .duration_since(UNIX_EPOCH)

View on GitHub (pinned to a2e22f3302)

Solutions

  1. Inspect the printed source path and confirm SKILL.md is missing or misnamed (e.g. skill.md, SKILL.MD)
  2. Restore SKILL.md (restore from backup or reinstall/re-import the skill to rewrite the whole SSOT copy)
  3. If the directory is junk, delete it and remove the DB row so sync stops attempting it

Example fix

// before
service.sync_to_app_dir(&directory, &app)?; // Skill 源目录缺少 SKILL.md

// after
let source = ssot_dir.join(&directory);
if !source.join("SKILL.md").is_file() {
    return reinstall_skill(&directory); // repopulate manifest before syncing
}
service.sync_to_app_dir(&directory, &app)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust caller — require the manifest before sync
let source = ssot_dir.join(&directory);
if !source.join("SKILL.md").is_file() {
    return Err(anyhow!("skill source incomplete (no SKILL.md): {}", source.display()));
}

Try / catch

match service.sync_to_app_dir(&dir, &app) {
    Err(e) if e.to_string().contains("缺少 SKILL.md") => repair_or_reinstall(&dir).await,
    other => other,
}

Prevention

When it happens

Trigger: An interrupted download/import left the SSOT directory half-populated; the user or another tool deleted SKILL.md from the SSOT folder; a directory was created manually inside SSOT that is not actually a skill.

Common situations: Disk-full during install so SKILL.md was never written; sync tools that skip the manifest file; users editing skills and accidentally renaming SKILL.md.

Related errors


AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16). Data as JSON: /api/errors/6db282a3c0179f2a. Report an issue: GitHub.