jdx/mise · error

brew-cask: app target '{target_name}' must be an absolute pa

Error message

brew-cask: app target '{target_name}' must be an absolute path

What it means

When a cask `app target:` contains a `/` it must be an absolute path; relative multi-component targets are ambiguous and could escape containment, so `app_target_path` rejects them. Bare names without `/` (e.g. `Foo.app`) are fine and are joined to the app dir. This error fires for values like `Foo/Bar.app` without a leading slash.

Source

Thrown at src/system/packages/brew/cask/paths.rs:146

            if path.starts_with(&app_dir) || path.starts_with(&prefix_app_dir) {
                return Ok(path);
            }
            // Casks routinely hardcode an absolute `/Applications/Foo.app`
            // target. When an override appdir is configured, relocate such a
            // target into it (preserving any subdirectories) rather than
            // rejecting it. `$HOMEBREW_PREFIX`-anchored targets are handled by
            // the check above and are never relocated.
            if app_dir != Path::new(DEFAULT_APP_DIR)
                && let Ok(rest) = path.strip_prefix(DEFAULT_APP_DIR)
            {
                return Ok(app_dir.join(rest));
            }
            bail!(
                "brew-cask: app target '{target_name}' must be under {}",
                app_dir.display()
            );
        }
        bail!("brew-cask: app target '{target_name}' must be an absolute path");
    }
    Ok(app_dir.join(target_name))
}

/// The directory `app` artifacts are linked into: `/Applications` unless
/// [`APP_DIR_ENV`] overrides it.
///
/// The override is validated here rather than at the point of use because
/// `app_target_path` treats the result as a containment boundary for symlinks
/// that may be created with elevated privileges. An empty value falls back to
/// the default so that exporting `MISE_BREW_CASK_OPT_APPDIR=` cannot disable
/// that boundary: `Path::starts_with("")` is true for every path.
pub(super) fn target_app_dir() -> Result<PathBuf> {
    let Ok(dir) = crate::env::var(APP_DIR_ENV) else {
        return Ok(PathBuf::from(DEFAULT_APP_DIR));
    };
    if dir.is_empty() {
        return Ok(PathBuf::from(DEFAULT_APP_DIR));

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Prefix the target with `/` (or the appropriate absolute root) so it is an absolute path, e.g. `/Applications/Subdir/Foo.app`
  2. Use a plain app-bundle name without `/` and let mise place it in the app dir: `Foo.app`
  3. Use `$HOMEBREW_PREFIX/...` or `$APPDIR/...` placeholders if the target is meant to be relative to those roots

Example fix

// before (cask stanza)
app target: 'Utilities/Foo.app'
// after
app target: '/Applications/Utilities/Foo.app'
Defensive patterns

Strategy: validation

Validate before calling

fn app_target_is_absolute_or_bare(target: &str) -> bool {
    !target.contains('\0') && (!target.contains('/') || target.starts_with('/') || target.starts_with("$HOMEBREW_PREFIX/") || target.starts_with("$APPDIR/"))
}

Try / catch

match result {
    Err(e) if e.to_string().contains("must be an absolute path") => {
        eprintln!("add a leading '/' or drop the '/' to use a bare bundle name");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling install/validation via `app_target_path` with a target string containing `/` but not starting with `/`, `$HOMEBREW_PREFIX/`, or `$APPDIR/` — e.g. `target: 'Subdir/Foo.app'`.

Common situations: Typo dropping the leading slash from an absolute target; hand-written custom cask with a nested relative target; misuse of `$HOMEBREW_PREFIX` placeholder spelling.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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