Hmbown/CodeWhale · error
refusing to update skill outside Codewhale-owned roots
Error message
refusing to update skill outside Codewhale-owned roots
What it means
`update_skill` only mutates skills living in roots marked writable/Codewhale-owned (`root.is_writable_owned()`). Bundled skills, external skills, and read-only locations are refused, since Codewhale must not edit content it does not own.
Solutions
- Import the external skill into an owned scope first, then update the imported copy.
- Target a skill that lives under a writable Codewhale-owned skills directory.
- Fix the skills directory configuration/permissions so the root is writable and recognized as owned.
Example fix
// before
mutation.execute(Mutation::Update { skill_id: bundled_skill_id, .. })?; // rejected
// after
mutation.execute_sync(Mutation::ImportExternal { source_id: external_id, target, conflict_policy })?;
mutation.execute(Mutation::Update { skill_id: imported_id, .. })?; Defensive patterns
Strategy: validation
Validate before calling
let (skill, _) = find_audited_skill(ctx, &skill_id)?;
if !skill.root.is_writable_owned() {
return Err(anyhow!("skill root is not a writable owned root; import first"));
} Type guard
fn updatable(skill: &AuditedSkill) -> bool {
skill.root.is_writable_owned() && skill.source_kind == SkillSourceKind::CodeWhaleManaged
} Prevention
- Only update skills shown as managed in the audit listing.
- Ensure the configured skills directory is writable.
- Import external skills before attempting updates.
When it happens
Trigger: Calling the `Update` mutation (`execute` → `update_skill`) for a skill whose root is not a writable owned root — a bundled skill, an unimported external skill, or an owned dir mounted read-only.
Common situations: Trying to update an external skill instead of importing it first; pointing the skills dir at a read-only path (NFS, container image layer); passing the ID of a bundled example skill.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- only Codewhale managed skills can be removed
- only CodeWhale managed skills can be trusted
- only Codewhale managed skills can be updated
- refusing to mutate non-owned root
- refusing to remove skill outside Codewhale-owned roots
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/d6b1a9888bbf3478.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/skills/mutation.rs:864
copy_dir_regular_files(&path, &target)?;
} else if meta.is_file() {
if name_str.starts_with('.') {
continue;
}
fs::copy(&path, &target)?;
}
}
Ok(())
}
async fn update_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 update skill outside Codewhale-owned roots");
}
if skill.source_kind != SkillSourceKind::CodeWhaleManaged {
bail!("only Codewhale managed skills can be updated");
}
let skills_dir = validate_owned_skill_path(ctx, &skill, &path)?;
// Imported skills carry `import:…` provenance and must not hit the registry.
ensure_remote_updatable(&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)?;
let outcome = install::update_with_registry(
package_name,View on GitHub (pinned to 73e0f67d83)