jdx/mise · error

brew-cask: completion target '{}' already exists and is not

Error message

brew-cask: completion target '{}' already exists and is not owned by cask '{}'

What it means

The completion link target already exists and is not a symlink (a regular file or directory). A completion target may only be replaced when it is a symlink whose ownership this cask can prove; a foreign real file is never clobbered, so the install aborts with the cask token in the message.

Source

Thrown at src/system/packages/brew/cask.rs:4146

    }
    if let Some(parent) = target.parent() {
        create_dir_all_elevating(parent)?;
    }
    ensure_completion_target_replaceable(cask, artifacts, target)?;
    make_symlink_elevating(&caskroom_completion, target)?;
    Ok(())
}

fn ensure_completion_target_replaceable(
    cask: &Cask,
    artifacts: &CaskArtifacts,
    target: &Path,
) -> Result<()> {
    let Ok(metadata) = target.symlink_metadata() else {
        return Ok(());
    };
    if !metadata.file_type().is_symlink() {
        bail!(
            "brew-cask: completion target '{}' already exists and is not owned by cask '{}'",
            target.display(),
            cask.token
        );
    }
    let link_target = std::fs::read_link(target)?;
    let resolved = resolve_symlink_target(target, link_target);
    let token_dir = caskroom_token_dir(&cask.token);
    if path_starts_with_resolved_root(&resolved, &token_dir) {
        return Ok(());
    }
    for completion in &artifacts.completions {
        if completion.target_path()? != target {
            continue;
        }
        if let Some(source) = appdir_artifact_source(&completion.source, &artifacts.apps)?
            && file::same_file(&resolved, &source)
        {

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Delete or rename the non-symlink file at the completion target after confirming it is not needed
  2. Change the completion artifact's target to a cask-unique file name
  3. Adjust user setup scripts to symlink rather than copy completions so ownership checks pass

Example fix

# shell - resolve the foreign regular file before reinstalling
ls -l ~/.zsh/completions/_myapp   # regular file, not a symlink
mv ~/.zsh/completions/_myapp ~/.zsh/completions/_myapp.bak
Defensive patterns

Strategy: validation

Validate before calling

// Only proceed when the completion target is absent or a symlink we may own
match target.symlink_metadata() {
    Err(_) => Ok(()),
    Ok(m) if m.file_type().is_symlink() => Ok(()),
    Ok(_) => Err(eyre!("completion target '{}' is a real file/dir; move it aside first", target.display())),
}

Try / catch

if err.to_string().contains("completion target") && err.to_string().contains("is not owned by cask") {
    // surface the path and the cask token so the user can relocate the foreign file
}

Prevention

When it happens

Trigger: A user dotfiles setup or another package copied a real completion file to the target path; the target path exists as a directory rather than a link.

Common situations: Dotfiles repositories that `cp` completions into place; a distro or other package manager installing the same completion name; README instructions telling users to copy the completion file manually.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/9a5033eacdee72f7. Report an issue: GitHub.