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 filesystemView on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Export an absolute path: `export MISE_BREW_CASK_OPT_APPDIR="$HOME/Applications"`
- Or unset the variable to use the default /Applications
- Check for stray exports: `env | grep MISE_BREW_CASK_OPT_APPDIR`
- 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
- Always export MISE_BREW_CASK_OPT_APPDIR as an absolute, tilde-expanded path: export MISE_BREW_CASK_OPT_APPDIR="$HOME/Applications"
- Add a shell-config sanity check: case "$MISE_BREW_CASK_OPT_APPDIR" in /*) ;; ""|*) echo "appdir must be absolute" >&2;; esac
- Leave the variable unset to use /Applications
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
- brew-cask: {APP_DIR_ENV} '{}' must not contain '..'
- brew-cask: {APP_DIR_ENV} '{}' must not resolve to the filesy
- brew-cask: invalid appdir '{}'
- brew-cask: app target '{target_name}' must be under {}
- install from exe
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/1d76d9d88054aa35.
Report an issue: GitHub.