jdx/mise · error

brew-cask: completion target '{}' must be under {}

Error message

brew-cask: completion target '{}' must be under {}

What it means

After rejecting `..` components, mise enforces that the resolved completion target still starts with the expected prefix (the shell's default completion directory). This is a second containment check: even without traversal components, a target that resolves outside the managed tree (e.g. via absolute construction or prefix mismatch) is rejected to keep cask completions inside mise's completion directories.

Source

Thrown at src/system/packages/brew/cask/mod.rs:2961

    let path = PathBuf::from(&target_name);
    let target = if path.is_absolute() {
        path
    } else if target_name.contains('/') {
        prefix.join(path)
    } else {
        default_completion_dir(shell).join(completion_filename(shell, &target_name)?)
    };
    if target
        .components()
        .any(|component| matches!(component, Component::ParentDir))
    {
        bail!(
            "brew-cask: completion target '{}' must not contain '..'",
            target.display()
        );
    }
    if !target.starts_with(&prefix) {
        bail!(
            "brew-cask: completion target '{}' must be under {}",
            target.display(),
            prefix.display()
        );
    }
    Ok(target)
}

fn generated_completion_target_path(shell: CompletionShell, base_name: &str) -> Result<PathBuf> {
    match shell {
        CompletionShell::Pwsh => {
            let name = format!("_{}.ps1", base_name);
            completion_target_path(shell, &name)
        }
        _ => completion_target_path(shell, base_name),
    }
}

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Make the cask's completion target relative so it lands under the shell's default completion directory
  2. Check your HOME/completion-dir environment so the resolved prefix matches expectations
  3. If you need completions elsewhere, link them yourself manually after install rather than via the cask stanza

Example fix

// before (cask stanza)
completion "/usr/local/share/fish/vendor_completions.d/x.fish"
// after
completion "x.fish"  # goes under the managed fish completions dir
Defensive patterns

Strategy: validation

Validate before calling

let prefix = std::path::Path::new("<expected-completion-dir>");
assert!(target_path.starts_with(prefix), "completion target must stay under the shell's completion dir");

Type guard

fn stays_under(prefix: &std::path::Path, target: &std::path::Path) -> bool {
  target.starts_with(prefix)
}

Try / catch

if let Err(e) = result {
  if e.contains("must be under") {
    eprintln!("completion target escaped the managed tree; make it relative so it lands under the default completion dir");
  }
}

Prevention

When it happens

Trigger: `!target.starts_with(&prefix)` where `prefix` is the expected completion directory for the shell — the computed target (e.g. `default_completion_dir(shell).join(...)`) resolved to a path not under that prefix.

Common situations: A cask overrides the completion directory to somewhere outside mise's managed tree; an environment/`HOME` mismatch makes the resolved target diverge from the expected prefix; a custom completion target on a different filesystem or volume-style path.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/a768c3cf2c2c3fc0. Report an issue: GitHub.