farion1231/cc-switch · error · anyhow::Error
SKILL_DIRECTORY_CONFLICT
SKILL_DIRECTORY_CONFLICT
Error message
{"code":"SKILL_DIRECTORY_CONFLICT","context":{"directory":"{directory}","existing_repo":"{existing_repo}","new_repo":"{new_repo}"},"suggestion":"uninstallFirst"} What it means
Structured skill-install error from CC Switch's Rust backend. During install(), reuse_existing_install (src-tauri/src/services/skill.rs:708-754) finds an already-installed skill whose install directory matches (case-insensitive eq_ignore_ascii_case) but which came from a DIFFERENT GitHub repo (repo_owner/repo_name mismatch). Same-repo reinstalls are idempotent (they just enable the app and sync); cross-repo ones return this JSON error built by format_skill_error (src-tauri/src/error.rs:127) with code SKILL_DIRECTORY_CONFLICT, context {directory, existing_repo, new_repo}, and suggestion 'uninstallFirst'.
Source
Thrown at src-tauri/src/services/skill.rs:735
continue;
}
let same_repo = existing.repo_owner.as_deref() == Some(&skill.repo_owner)
&& existing.repo_name.as_deref() == Some(&skill.repo_name);
if same_repo {
let mut updated = existing.clone();
updated.apps.set_enabled_for(current_app, true);
db.save_skill(&updated)?;
Self::sync_to_app_dir(&updated.directory, current_app)?;
log::info!(
"Skill {} 已存在,更新 {:?} 启用状态",
updated.name,
current_app
);
return Ok(Some(updated));
}
return Err(anyhow!(format_skill_error(
"SKILL_DIRECTORY_CONFLICT",
&[
("directory", install_name),
(
"existing_repo",
&format!(
"{}/{}",
existing.repo_owner.as_deref().unwrap_or("unknown"),
existing.repo_name.as_deref().unwrap_or("unknown")
)
),
(
"new_repo",
&format!("{}/{}", skill.repo_owner, skill.repo_name)
),
],
Some("uninstallFirst"),
)));View on GitHub (pinned to a2e22f3302)
Solutions
- Uninstall the existing skill first, then retry the install (the error's suggestion: uninstallFirst)
- Or install from the original repo reported in context.existing_repo instead of the new one
- In UI code, parse the JSON and offer a one-click 'uninstall and reinstall' flow keyed on suggestion === 'uninstallFirst'
Example fix
// before
await invoke("install_skill", { skill }); // SKILL_DIRECTORY_CONFLICT
// after
await invoke("uninstall_skill", { directory: conflict.context.directory });
await invoke("install_skill", { skill }); Defensive patterns
Strategy: try-catch
Validate before calling
// Frontend: before install, check for a same-directory skill from another repo
const installed = await invoke<InstalledSkill[]>("get_all_installed_skills");
const clash = installed.find(
(s) => s.directory.toLowerCase() === skill.directory.split("/").pop()?.toLowerCase(),
);
if (clash && `${clash.repoOwner}/${clash.repoName}` !== `${skill.repoOwner}/${skill.repoName}`) {
promptUninstallFirst(clash.directory); // SKILL_DIRECTORY_CONFLICT would be thrown
} Type guard
interface SkillError {
code: string;
context: Record<string, string>;
suggestion?: string;
}
function isSkillError(value: unknown): value is SkillError {
if (typeof value !== "string") return false;
try {
const parsed = JSON.parse(value);
return typeof parsed?.code === "string" && typeof parsed?.context === "object";
} catch {
return false;
}
} Try / catch
try {
await install(skill);
} catch (e) {
if (isSkillError(e) && e.code === "SKILL_DIRECTORY_CONFLICT") {
// e.context.existing_repo vs e.context.new_repo; e.suggestion === "uninstallFirst"
await offerUninstallAndReinstall(e.context.directory);
return;
}
throw e;
} Prevention
- Check the installed list for directory collisions (case-insensitive) before installing
- Remember same-repo reinstalls are idempotent — conflicts only occur across different owner/repo
- When switching a skill to a fork, uninstall the original first
When it happens
Trigger: Installing a skills.sh entry named e.g. 'pdf' when a 'pdf' directory already exists in the SSOT installed by another owner/repo; case variants like 'PDF' vs 'pdf' also collide because the comparison ignores ASCII case.
Common situations: Popular skills forked across many repos (agents skills collections); users migrating from one fork to another without uninstalling; two marketplace entries with the same skillId from different repos (dedup key is owner/repo:directory, so both are listed).
Related errors
- INVALID_SKILL_DIRECTORY
- DOWNLOAD_TIMEOUT
- SKILL_DIR_NOT_FOUND
- Skill 存储目录不能与 {app:?} 的 Skills 目录相同: {}
- Skill directory changed during install; please retry
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/3bc7f51707c6f0b8.
Report an issue: GitHub.