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

MCode Skill 与托管副本内容不同,拒绝替换链接

Error message

MCode Skill 与托管副本内容不同,拒绝替换链接: {dir_name}

What it means

When syncing a skill into the MCode (MiniMax Code) app directory, the code finds the native destination is a symlink that does not alias the intended target. It then compares content hashes of the linked (managed) copy and the new source; if they differ, replacement is refused to avoid clobbering a symlink whose pointed-to content diverged from the deployed skill. This guards against replacing a link that no longer matches managed content.

Solutions

  1. Diff the content at the native symlink target against the new skill source; if the local edits are unwanted, delete the divergent content and re-run the sync so the link is replaced.
  2. If the local modifications are wanted, rename/backup the divergent directory, then re-run deployment to establish a fresh managed link.
  3. Verify the symlink actually points at the intended managed copy (paths_alias check) — a wrong symlink target is often the root cause.
  4. Re-import or re-hash the skill so its deployment hash matches, then retry.

Example fix

// before: sync fails because the linked copy was hand-edited
sync_skill_to_mcode(dir_name)?;
// after: back up the divergent copy and re-sync
fs::rename(&native, native.with_extension("bak"));
sync_skill_to_mcode(dir_name)?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_replacable(native: &Path, dest: &Path) -> bool {
    if !is_symlink(native) || paths_alias(native, dest) { return true; }
    compute_pi_deployment_hash(native) == compute_pi_deployment_hash(dest)
}

Try / catch

if let Err(e) = sync_skill_to_mcode(dir_name) {
    if e.to_string().contains("拒绝替换链接") {
        backup_and_remove_link(native)?;
        sync_skill_to_mcode(dir_name)?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Deploying/importing a skill where the app's skills dir entry for dir_name is a symlink pointing somewhere other than the SSOT path, AND compute_pi_deployment_hash(native) != compute_pi_deployment_hash(dest) — i.e. the linked copy's files were modified after deployment so it no longer matches the incoming skill content.

Common situations: A user manually edited files inside the symlinked skill directory; the skill was previously linked to a different/custom skill source; an older deployment left a stale symlink after the SSOT content was updated; switching between managed and hand-crafted versions of the same skill name.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of farion1231/cc-switch@06082e189d (2026-09-19). Data as JSON: /api/errors/18741fd1b37a29fd. Report an issue: GitHub.

Appendix: source

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

            // 解析元数据
            let skill_md = dest.join("SKILL.md");
            let (name, description) = Self::read_skill_name_desc(&skill_md, &dir_name);

            // 其他应用保存用户选择;Pi 的 exists=active 必须直接来自原生目录。
            let mut apps = selection.apps;
            apps.pi = Self::skill_exists_in_app(&dir_name, &AppType::Pi);

            // An explicitly imported MCode link must point at the managed copy
            // so subsequent toggles and updates can verify its ownership.
            if apps.mcode {
                let projection = (|| -> Result<()> {
                    let native = Self::get_distinct_app_skills_dir(&ssot_dir, &AppType::Mcode)?
                        .join(&dir_name);
                    if Self::is_symlink(&native) && !Self::paths_alias(&native, &dest) {
                        if Self::compute_pi_deployment_hash(&native)?
                            != Self::compute_pi_deployment_hash(&dest)?
                        {
                            return Err(anyhow!(
                                "MCode Skill 与托管副本内容不同,拒绝替换链接: {dir_name}"
                            ));
                        }
                        if let Some(deployment) =
                            Self::inspect_pi_skill_destination(&native, &native, &dir_name)?
                        {
                            Self::refresh_pi_skill_destination(
                                &dest,
                                &native,
                                &dir_name,
                                &deployment,
                            )?;
                        }
                    } else {
                        Self::preflight_install_destination(&dest, &dir_name, &AppType::Mcode)?;
                    }
                    if !native.exists() {
                        Self::sync_to_app_dir(&dir_name, &AppType::Mcode)?;

View on GitHub (pinned to 06082e189d)