jdx/mise · error · eyre::Report
brew-cask: {APP_DIR_ENV} '{}' must not contain '..'
Error message
brew-cask: {APP_DIR_ENV} '{}' must not contain '..' What it means
After confirming MISE_BREW_CASK_OPT_APPDIR is absolute, target_app_dir rejects any value whose components contain '..'. Because the app dir is used as a symlink-free containment boundary for privileged writes, lexical parent-directory components would let a cask escape it. This mirrors the '..' checks applied to individual cask targets.
Source
Thrown at src/system/packages/brew/cask.rs:6042
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
// root (`/`, `//`, `/.`, a symlink to `/`, ...) to `/` so they can all be
// rejected together.
let resolved = resolve_appdir(&dir);
if !resolved
.components()
.any(|component| matches!(component, Component::Normal(_)))
{
bail!(
"brew-cask: {APP_DIR_ENV} '{}' must not resolve to the filesystem root",View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Export the normalized path without '..': `export MISE_BREW_CASK_OPT_APPDIR=/opt/MyApps`
- Generate the value with realpath: `export MISE_BREW_CASK_OPT_APPDIR="$(realpath ~/Applications)"`
- Or unset the variable to fall back to /Applications
Example fix
# before export MISE_BREW_CASK_OPT_APPDIR=/Applications/../opt/MyApps # after export MISE_BREW_CASK_OPT_APPDIR=/opt/MyApps
Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Component, Path};
fn appdir_env_components_ok(val: &str) -> bool {
let p = Path::new(val);
p.is_absolute() && !p.components().any(|c| matches!(c, Component::ParentDir))
} Try / catch
match target_app_dir() {
Ok(dir) => dir,
Err(e) if e.to_string().contains("must not contain '..'") => {
let clean = std::path::PathBuf::from(val).canonicalize()?;
std::env::set_var("MISE_BREW_CASK_OPT_APPDIR", clean);
target_app_dir()
}
Err(e) => Err(e),
} Prevention
- Normalize the appdir with realpath before exporting it
- Never build the value by string concatenation of fragments that may include '..'
When it happens
Trigger: Exporting MISE_BREW_CASK_OPT_APPDIR="/Applications/../opt/MyApps" or any absolute path containing a '..' component.
Common situations: Users shortening paths with '..' in dotfiles or CI env; scripts concatenating path fragments that produce '..' segments.
Related errors
- brew-cask: {APP_DIR_ENV} '{}' must be an absolute path
- brew-cask: {APP_DIR_ENV} '{}' must not resolve to the filesy
- brew-cask: invalid appdir '{}'
- brew-cask:{}: invalid {kind} {field} path {}
- brew-cask: app target '{target_name}' must not contain '..'
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/ec6713fb5c55a3e3.
Report an issue: GitHub.