jdx/mise · error · eyre::Report

brew-cask: {APP_DIR_ENV} '{}' must be an absolute path

Error message

brew-cask: {APP_DIR_ENV} '{}' must be an absolute path

What it means

target_app_dir reads MISE_BREW_CASK_OPT_APPDIR (see APP_DIR_ENV, src/system/packages/brew/cask.rs:41) to override the default /Applications app dir. An empty value deliberately falls back to the default, but a non-empty value that is not absolute is rejected. The app dir is the containment boundary for privileged cask linking, so a relative value would make the boundary machine-dependent and unsafe.

Source

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

/// 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.
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));
    }
    let dir = PathBuf::from(dir);
    if !dir.is_absolute() {
        bail!(
            "brew-cask: {APP_DIR_ENV} '{}' must be an absolute path",
            dir.display()
        );
    }
    if dir
        .components()
        .any(|component| matches!(component, Component::ParentDir))
    {
        bail!(
            "brew-cask: {APP_DIR_ENV} '{}' must not contain '..'",
            dir.display()
        );
    }
    // Resolve the override to a real absolute path: canonicalize its longest
    // existing prefix and re-append the components that do not exist yet. This
    // makes the appdir a symlink-free containment boundary — privileged cask
    // mutations then operate on resolved paths and cannot be redirected through
    // a symlinked component — and it collapses every spelling of the filesystem

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Export an absolute path: `export MISE_BREW_CASK_OPT_APPDIR="$HOME/Applications"`
  2. Or unset the variable to use the default /Applications
  3. Check for stray exports: `env | grep MISE_BREW_CASK_OPT_APPDIR`
  4. After fixing, re-run the mise cask install/upgrade

Example fix

# before
grep MISE_BREW_CASK_OPT_APPDIR ~/.zshrc
export MISE_BREW_CASK_OPT_APPDIR=~/Applications
# after
export MISE_BREW_CASK_OPT_APPDIR="$HOME/Applications"
Defensive patterns

Strategy: validation

Validate before calling

fn appdir_env_valid(val: Option<&str>) -> bool {
    match val {
        None | Some("") => true, // empty falls back to default
        Some(dir) => std::path::Path::new(dir).is_absolute(),
    }
}
// before install:
if !appdir_env_valid(std::env::var("MISE_BREW_CASK_OPT_APPDIR").ok().as_deref()) {
    eprintln!("MISE_BREW_CASK_OPT_APPDIR must be an absolute path");
}

Try / catch

match target_app_dir() {
    Ok(dir) => dir,
    Err(e) if e.to_string().contains("must be an absolute path") => {
        std::env::remove_var("MISE_BREW_CASK_OPT_APPDIR");
        target_app_dir() // retry with the default /Applications
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Exporting MISE_BREW_CASK_OPT_APPDIR with a relative value such as "Applications" or a quoted "~/Applications" (tilde not expanded by the shell).

Common situations: Shell config (.zshrc/.bashrc) exporting the var with ~ inside quotes or a shorthand relative path; CI scripts setting the var from a relative working-dir path.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/1d76d9d88054aa35. Report an issue: GitHub.