Hmbown/CodeWhale · error
skill ' ' has multiple owned packages in the target scope…
Error message
skill '{}' has multiple owned packages in the target scope; remove or consolidate them before importing What it means
Before copying, `import_external` resolves exactly one owned package matching the source skill's canonical name in the target scope. If the on-disk scan finds more than one owned package for that name, the operation aborts because it cannot determine a unique destination; consolidating ownership is a user decision.
Solutions
- Delete or consolidate the duplicate owned package directories so only one remains, then retry the import.
- Rename one package's SKILL.md name (or directory) so the canonical names no longer collide.
- Choose a different `SkillTargetScope` that contains only one package for this name.
Example fix
// before skills/my-skill/ (SKILL.md name: my-skill) ~/.codewhale/skills/my-skill/ (SKILL.md name: my-skill) // after ~/.codewhale/skills/my-skill/ (single owned package; duplicate removed)
Defensive patterns
Strategy: validation
Validate before calling
let matches: Vec<_> = owned_snap.skills.iter().filter(|s| s.id.canonical_name == source_id.canonical_name).collect();
if matches.len() > 1 {
return Err(anyhow!("multiple owned packages for {}; consolidate first", source_id.canonical_name));
} Prevention
- Keep one owned package per canonical skill name per scope.
- Clean up old directories after renaming a skill.
- Avoid copying skill folders manually; use import/remove mutations.
When it happens
Trigger: Two or more owned package directories map to the same canonical skill name in the target scope (duplicate dirs whose SKILL.md names collide, or the same name in overlapping owned roots) while calling `import_external`.
Common situations: Manually copied skill folders with duplicate SKILL.md `name` fields; a skill imported earlier into both user and project scopes; a renamed directory that left the old copy behind so two dirs claim one name.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- destination already exists with different content
- bundle contains conflicting or rejected entries
- import source must be a compatible external skill
- Runtime import contains multiple threads. Export one thread…
- skill is not an import candidate
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/df4bb17630192b70.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/skills/mutation.rs:701
.skills
.iter()
.filter(|s| s.id.canonical_name == source_id.canonical_name)
.filter(|s| s.root.kind == want_kind)
.collect();
// Prefer the on-disk directory of an existing same-scope package so we
// replace/reject against Hello_World rather than creating a parallel
// hello-world next to it.
let (dest, dest_segment) = match existing_in_target.as_slice() {
[] => {
let segment = source_id.canonical_name.clone();
(skills_dir.join(&segment), segment)
}
[only] => {
let segment = on_disk_package_name(&only.id)?.to_string();
(only.root.path.join(&only.id.relative_dir), segment)
}
_ => bail!(
"skill '{}' has multiple owned packages in the target scope; \
remove or consolidate them before importing",
source_id.canonical_name
),
};
let dest_exists = validate_owned_child(&skills_dir, &dest, false)?;
if dest_exists {
let existing = package_digest::compute_package_digest(&dest).ok();
if existing.as_deref() == Some(expected_digest.as_str()) {
return Ok(SkillMutationReceipt {
action: SkillActionKind::Import,
name: source_id.canonical_name.clone(),
scope: target.as_skill_scope(),
safe_target_path: safe_display_path(&dest, Some(ctx.workspace), ctx.home),
before_digest: before,
after_digest: existing,
outcome: SkillMutationOutcome::AlreadyPresent,View on GitHub (pinned to 73e0f67d83)