BigPizzaV3/CodexPlusPlus · error · anyhow::Error

skill 源目录不存在:

Error message

skill 源目录不存在:{}

What it means

set_enabled materializes a skill into the linked directory by creating a symlink/copy from the skill's source directory. If the expected source directory (source_dir joined with the skill id) does not exist or is not a directory, enabling is aborted with this message.

Solutions

  1. Verify the skill directory exists at <source_dir>/<id> and re-add it if deleted
  2. Check the configured skills source directory path in settings points to the right root
  3. Reinstall the skill (install_from_zip or manual copy) before enabling it
  4. Confirm the skill id casing matches the on-disk directory name exactly

Example fix

// before
skills.set_enabled("my-skill", true)?; // 源目录已删
// after
let src = skills_root.join("my-skill");
if !src.is_dir() {
    // 重新安装或修正 skills 根目录后再启用
    anyhow::bail!("请先安装 skill my-skill 到 {}", skills_root.display());
}
skills.set_enabled("my-skill", true)?;
Defensive patterns

Strategy: validation

Validate before calling

let src = skills_root.join(id);
if !src.is_dir() {
    anyhow::bail!("skill 源目录不存在:{}", src.display());
}

Type guard

fn skill_source_exists(skills_root: &Path, id: &str) -> bool {
    skills_root.join(id).is_dir()
}

Try / catch

match skills.set_enabled(id, true) {
    Err(e) if e.to_string().starts_with("skill 源目录不存在") => eprintln!("请先重新安装该 skill"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling set_enabled(id, true) (directly or via install_from_zip / restore_backup) when self.source_dir.join(id) is missing or is a file, not a directory.

Common situations: Skill was deleted from the source dir while still registered; a wrong skills root directory is configured; a zip install was cleaned up or a backup restore references a removed path; case-mismatched skill id on a case-sensitive filesystem.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/5c4e614bf6661cb0. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/skills.rs:442

            },
        );
        self.save_state_unlocked(&state)?;
        drop(_guard);

        self.set_enabled(&skill.id, true)?;
        Ok(self.load_state())
    }

    /// 启用 = 在 `$CODEX_HOME/skills/` 下建一个指向源目录的软链;停用 = 删掉它。
    pub fn set_enabled(&self, id: &str, enabled: bool) -> anyhow::Result<()> {
        validate_skill_id(id)?;
        let source = self.source_dir.join(id);
        let link = self.linked_dir().join(id);
        if !enabled {
            return remove_link(&link);
        }
        if !source.is_dir() {
            anyhow::bail!("skill 源目录不存在:{}", source.display());
        }
        std::fs::create_dir_all(self.linked_dir())
            .with_context(|| format!("创建 {} 失败", self.linked_dir().display()))?;
        remove_link(&link)?;
        link_or_copy(&source, &link)
    }

    /// 卸载:删软链,源目录整体移进备份目录。不做自动轮转删除。
    pub fn uninstall(&self, id: &str) -> anyhow::Result<SkillsState> {
        validate_skill_id(id)?;
        remove_link(&self.linked_dir().join(id))?;

        let source = self.source_dir.join(id);
        if source.is_dir() {
            std::fs::create_dir_all(&self.backups_dir)
                .with_context(|| format!("创建备份目录失败:{}", self.backups_dir.display()))?;
            let backup = self
                .backups_dir

View on GitHub (pinned to b1ed92e5e4)