Hmbown/CodeWhale · error
audited owned root does not match mutation target
Error message
audited owned root {} does not match mutation target {} What it means
validate_owned_root_descriptor re-derives the expected owned skills root for the descriptor's scope (project or global) and compares it with the path recorded in the audited root descriptor. A mismatch means the audit snapshot is stale or points at a different location than the mutation would resolve, so the mutation is refused to avoid writing outside the audited location.
Solutions
- Re-run skill discovery/audit in the current workspace+home so the root descriptor path matches the resolved target, then retry the mutation.
- Check that the workspace path and HOME are the same as when the skill was audited; correct the invocation's workspace/home context.
- If config changed the skills root, reload config and rebuild the audited skill list before mutating.
Example fix
// before let audited = load_cached_audit(old_workspace); update_skill(&ctx, &audited.skill)?; // stale path // after let audited = rediscover_and_audit(&ctx.workspace, ctx.home)?; update_skill(&ctx, &audited.skill)?;
Defensive patterns
Strategy: validation
Validate before calling
fn audit_is_current(ctx: &MutationContext, root: &SkillRootDescriptor) -> bool {
match target_scope_for_root(root) {
Ok(scope) => resolve_owned_target(ctx.workspace, ctx.home, scope)
.map(|expected| expected == root.path)
.unwrap_or(false),
Err(_) => false,
}
} Try / catch
// match on the error to distinguish stale-audit from other failures
match update_skill(&ctx, &skill) {
Err(e) if e.to_string().contains("does not match mutation target") => {
let fresh = rediscover_and_audit(&ctx.workspace, ctx.home)?;
update_skill(&ctx, &fresh.skill)?
}
other => other?,
} Prevention
- Re-audit skills in the same process/workspace where you mutate; never persist AuditedSkill across moves.
- Keep workspace and HOME stable between discovery and mutation.
- Reload configuration after any change to skills root settings.
When it happens
Trigger: Calling validate_owned_skill_path or update_skill where root.path != resolve_owned_target(workspace, home, scope): e.g. the workspace directory changed between audit and mutation, HOME moved, the project was relocated, or a stale AuditedSkill from an old session is reused.
Common situations: Renaming/moving the repo or changing the workspace root after listing skills; running with a different $HOME; stale cached audit results after a config change; passing a global-scope skill audit into a mutation with a different home.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- owned skill path does not exist
- owned skill path escapes direct root
- refusing to mutate path
- audited skill root identity changed; refusing mutation
- fleet manager for run
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/ea93c92c575de4b6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/skills/mutation.rs:280
fn target_scope_for_root(root: &SkillRootDescriptor) -> Result<SkillTargetScope> {
if !root.is_writable_owned() {
bail!("refusing to mutate non-owned root {}", root.path.display());
}
match root.kind {
SkillRootKind::CodeWhaleProject => Ok(SkillTargetScope::Project),
SkillRootKind::CodeWhaleGlobal => Ok(SkillTargetScope::Global),
_ => bail!("refusing to mutate non-owned root {}", root.path.display()),
}
}
fn validate_owned_root_descriptor(
ctx: &MutationContext<'_>,
root: &SkillRootDescriptor,
) -> Result<PathBuf> {
let target = target_scope_for_root(root)?;
let expected = resolve_owned_target(ctx.workspace, ctx.home, target)?;
if root.path != expected {
bail!(
"audited owned root {} does not match mutation target {}",
root.path.display(),
expected.display()
);
}
let anchor = owned_anchor(ctx.workspace, ctx.home, target)?;
validate_owned_target_chain(anchor, &expected, true)?;
Ok(expected)
}
/// Validate a real direct child of an already validated owned skills root.
/// Returns false only when a missing child is permitted.
fn validate_owned_child(skills_dir: &Path, child: &Path, require_existing: bool) -> Result<bool> {
if child.parent() != Some(skills_dir) {
bail!(
"refusing to mutate path {} outside direct owned root {}",
child.display(),
skills_dir.display()View on GitHub (pinned to 73e0f67d83)