Hmbown/CodeWhale · error

only Codewhale managed skills can be removed

Error message

only Codewhale managed skills can be removed

What it means

remove_skill() only removes skills whose source_kind is SkillSourceKind::CodeWhaleManaged. The library deliberately refuses to delete skills it did not create (user-authored, imported, or external skills), even if they sit under a writable root, to avoid destroying assets outside its management contract. The check runs after resolving the audited skill and confirming its root is writable-owned.

Solutions

  1. Only call remove_skill on skills created/installed by Codewhale itself; check skill.source_kind == SkillSourceKind::CodeWhaleManaged first
  2. If the skill is user-authored, delete it manually from its skill directory instead of through this API
  3. Reinstall the skill through a Codewhale-managed flow so it is marked CodeWhaleManaged, then remove it
  4. If the skill should be managed but is not, fix its source metadata/registration rather than forcing removal

Example fix

// before
ctx.remove_skill(skill_id, digest)?; // panics on user-authored skill
// after
if skill.source_kind == SkillSourceKind::CodeWhaleManaged {
    ctx.remove_skill(skill_id, digest)?;
} else {
    std::fs::remove_dir_all(&skill.path)?; // manual removal for non-managed skills
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_managed(skill: &Skill) -> bool { skill.source_kind == SkillSourceKind::CodeWhaleManaged }

Type guard

fn is_managed_skill(s: &Skill) -> bool { matches!(s.source_kind, SkillSourceKind::CodeWhaleManaged) }

Prevention

When it happens

Trigger: Calling remove_skill (or remove_and_trust_require_managed_owned / remove_managed_skill / remove_uses_on_disk_dir_not_canonical_name via execute_sync) with a skill_id that resolves to a skill whose source_kind is not CodeWhaleManaged — e.g. a user-authored or imported skill.

Common situations: Developers try to programmatically clean up a directory of mixed skills and hit a skill that was hand-created on disk rather than installed through Codewhale; or a skill was created by an older tool version that marked source_kind differently.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/a7f22aa8bedd482a. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/skills/mutation.rs:945

            safe_target_path: safe_display_path(&path, Some(ctx.workspace), ctx.home),
            before_digest: before,
            after_digest: None,
            outcome: SkillMutationOutcome::NetworkDenied(host),
        }),
    }
}

fn remove_skill(
    skill_id: AuditedSkillId,
    expected_digest: Option<String>,
    ctx: &MutationContext<'_>,
) -> Result<SkillMutationReceipt> {
    let (skill, path) = find_audited_skill(ctx, &skill_id)?;
    if !skill.root.is_writable_owned() {
        bail!("refusing to remove skill outside Codewhale-owned roots");
    }
    if skill.source_kind != SkillSourceKind::CodeWhaleManaged {
        bail!("only Codewhale managed skills can be removed");
    }
    let skills_dir = validate_owned_skill_path(ctx, &skill, &path)?;
    let before = verify_expected_digest(&path, expected_digest.as_deref())?;
    let scope = match skill.root.kind {
        SkillRootKind::CodeWhaleProject => SkillScope::Project,
        SkillRootKind::CodeWhaleGlobal => SkillScope::Global,
        _ => SkillScope::Logical,
    };
    let package_name = on_disk_package_name(&skill_id)?;
    validate_owned_skill_path(ctx, &skill, &path)?;
    install::uninstall(package_name, &skills_dir)?;
    Ok(SkillMutationReceipt {
        action: SkillActionKind::Remove,
        name: skill_id.canonical_name,
        scope,
        safe_target_path: safe_display_path(&path, Some(ctx.workspace), ctx.home),
        before_digest: before,
        after_digest: None,

View on GitHub (pinned to 73e0f67d83)