{"record":{"id":"4b4033eb68a805d4","repo":"farion1231/cc-switch","slug":"skill-ssot-directory","errorCode":null,"errorMessage":"Skill 不存在于 SSOT: {directory}","messagePattern":"Skill 不存在于 SSOT: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src-tauri/src/services/skill.rs","lineNumber":2337,"sourceCode":"        if Self::is_symlink(path) {\n            // 符号链接：仅删除链接本身，不影响源文件\n            #[cfg(unix)]\n            fs::remove_file(path)?;\n            #[cfg(windows)]\n            fs::remove_dir(path)?; // Windows 的目录 symlink 需要用 remove_dir\n        } else if path.is_dir() {\n            // 真实目录：递归删除\n            fs::remove_dir_all(path)?;\n        } else if path.exists() {\n            // 普通文件\n            fs::remove_file(path)?;\n        }\n        Ok(())\n    }\n\n    fn validate_sync_source_dir(source: &Path, directory: &str) -> Result<()> {\n        if !source.is_dir() {\n            return Err(anyhow!(\"Skill 不存在于 SSOT: {directory}\"));\n        }\n\n        let manifest = source.join(\"SKILL.md\");\n        if !manifest.is_file() {\n            return Err(anyhow!(\n                \"Skill 源目录缺少 SKILL.md，拒绝同步以避免覆盖目标目录: {}\",\n                source.display()\n            ));\n        }\n\n        Ok(())\n    }\n\n    fn replace_dest_with_copy(source: &Path, dest: &Path, directory: &str) -> Result<()> {\n        Self::validate_sync_source_dir(source, directory)?;\n\n        let parent = dest\n            .parent()","sourceCodeStart":2319,"sourceCodeEnd":2355,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src-tauri/src/services/skill.rs#L2319-L2355","documentation":"Thrown by SkillService::validate_sync_source_dir during sync_to_app_dir: the skill's source directory under the SSOT store (single source of truth) does not exist or is not a directory, so the service refuses to propagate anything to the app's skills folder. It fails before any destination write, so the app-side copy is never touched. The {directory} placeholder is the install name stored in the installed_skills DB row.","triggerScenarios":"Calling sync_to_app_dir(directory, app) for a skill whose SSOT folder <ssot_dir>/<directory> was deleted, moved, or never created: files removed manually in the file manager, a cleaner/antivirus wiped app data, an interrupted install left a DB row without files, or the SSOT location setting changed after the skill was installed.","commonSituations":"DB restored from a WebDAV remote snapshot but SSOT files not yet materialized; user reorganized the SSOT directory by hand; partial uninstall; app-data migration between machines copying the SQLite DB but not the skills folder.","solutions":["Resolve the SSOT dir (SkillService::get_ssot_dir) and check whether <ssot>/<directory> exists; if it was moved, move it back or update the setting","If the files are gone, reinstall or re-import the skill so the SSOT copy is repopulated before syncing","If the DB row is stale (skill really removed), uninstall the skill / delete the row so sync attempts stop"],"exampleFix":"// before\nservice.sync_to_app_dir(&directory, &app)?; // errors: Skill 不存在于 SSOT\n\n// after\nlet source = SkillService::get_ssot_dir()?.join(&directory);\nif !source.is_dir() {\n    // prompt user to reinstall / re-import the skill instead of failing sync\n    return reinstall_skill(&directory);\n}\nservice.sync_to_app_dir(&directory, &app)?;","handlingStrategy":"validation","validationCode":"// Rust caller — verify SSOT source exists before requesting a sync\nlet ssot = SkillService::get_ssot_dir()?;\nlet source = ssot.join(&directory);\nif !source.is_dir() {\n    // trigger reinstall / re-import flow instead of letting sync fail\n    return Err(anyhow!(\"skill missing from SSOT: {}\", source.display()));\n}","typeGuard":null,"tryCatchPattern":"match service.sync_to_app_dir(&dir, &app) {\n    Err(e) if e.to_string().contains(\"Skill 不存在于 SSOT\") => reinstall_skill(&dir).await,\n    other => other,\n}","preventionTips":["Never delete or move SSOT skill folders by hand; uninstall via the app so the DB row is removed too","After restoring a DB from a remote snapshot, verify SSOT files are materialized before triggering sync-all","Monitor for rows whose SSOT dir is missing and clean them up promptly"],"tags":["skill","sync","filesystem","ssot"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}