Hmbown/CodeWhale · error

destination already exists with different content

Error message

destination {} already exists with different content

What it means

When the destination package already exists in the owned scope but its content digest differs from the imported source, `import_external` honors the configured `ConflictPolicy`. With `ConflictPolicy::Reject` the operation fails instead of overwriting existing content.

Solutions

  1. Pass `ConflictPolicy::Replace` to intentionally replace the differing destination.
  2. Delete or manually merge the existing destination package, then re-run the import with Reject.
  3. Update the external source to match if the owned copy is the authoritative version.
  4. Diff `dest` against the source to decide which content you actually want before re-importing.

Example fix

// before
mutation.execute_sync(Mutation::ImportExternal { source_id, target, conflict_policy: ConflictPolicy::Reject })?;
// after
mutation.execute_sync(Mutation::ImportExternal { source_id, target, conflict_policy: ConflictPolicy::Replace })?;
Defensive patterns

Strategy: try-catch

Validate before calling

if owned_dest_exists && owned_dest_digest != expected_digest && conflict_policy == ConflictPolicy::Reject {
    // resolve the conflict manually or switch to Replace before calling import
}

Try / catch

match err.downcast_ref::<String>() {
    Some(msg) if msg.starts_with("destination ") && msg.ends_with("already exists with different content") => {
        // retry with ConflictPolicy::Replace or merge manually
    }
    _ => return Err(err),
}

Prevention

When it happens

Trigger: Calling `import_external` with `conflict_policy == ConflictPolicy::Reject` while `dest` already exists and its package digest differs from `expected_digest` (the pre-copy source digest).

Common situations: Editing the skill in its external source after a previous import, then re-importing under Reject; concurrent edits to the owned copy; importing an older/newer version of the same skill over a modified owned copy.

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


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

Appendix: source

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

        ),
    };

    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,
            });
        }
        if conflict_policy == ConflictPolicy::Reject {
            bail!(
                "destination {} already exists with different content",
                dest.display()
            );
        }
    }

    // Re-verify source digest immediately before copy (TOCTOU).
    let _ = verify_expected_digest(&source_path, Some(&expected_digest))?;
    validate_owned_target_chain(anchor, &skills_dir, true)?;

    let staging = skills_dir.join(format!("{dest_segment}.tmp"));
    if validate_owned_child(&skills_dir, &staging, false)? {
        fs::remove_dir_all(&staging)
            .with_context(|| format!("failed to clean stale staging dir {}", staging.display()))?;
    }
    copy_skill_package(&source_path, &staging)?;
    validate_owned_target_chain(anchor, &skills_dir, true)?;
    validate_owned_child(&skills_dir, &staging, true)?;

View on GitHub (pinned to 73e0f67d83)