jdx/mise · error
brew-cask: invalid app source
Error message
brew-cask: invalid app source '{}' What it means
AppArtifact source validation rejects sources that contain NUL bytes or backslashes, end with '/', or cannot be expressed as a relative artifact path (relative_artifact_path returns None). This runs before taking the basename so that traversal tricks (embedded separators, directory escapes) cannot hide inside a nested archive source.
Solutions
- Fix the cask's source to be a clean relative path inside the archive (no backslashes, NULs, or trailing slash).
- Replace backslash separators with '/' if the definition was written on Windows.
- Point the source at the actual file/bundle rather than a directory.
- Verify the cask against its upstream tap if you did not author it.
Example fix
// before "source": ".\\Applications\\MyApp.app/" // after "source": "MyApp.app"
Defensive patterns
Strategy: validation
When it happens
Trigger: Resolving an app artifact whose `source` string contains '\0' or '\\', ends with '/', or is absolute/outside the staged archive (relative_artifact_path(Path::new(""), source) fails).
Common situations: Hand-edited cask JSON with Windows-style separators; a source accidentally written as a directory ('foo/') instead of a file; malicious or corrupted cask definitions attempting '../' traversal; conversion scripts emitting absolute paths.
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
- brew-cask: refusing generic artifact source outside the…
- app Info.plist must be a regular file
- brew-cask: cannot adopt
- brew-cask: : conflicts with installed cask
- brew-cask: : dependency cycle detected
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/514111c078d74060.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/mod.rs:899
remove_stale_versions(&caskroom_token, &cask.version)?;
remove_cask_journals(&cask.token)?;
file::remove_all(stage)?;
Ok(cask.version)
}
}
impl AppArtifact {
fn target_name(&self) -> Result<&str> {
if let Some(target) = &self.target {
return Ok(target);
}
// A nested archive source still installs as its bundle basename. Check
// the source before taking that basename so traversal cannot be hidden.
if self.source.contains(['\0', '\\'])
|| self.source.ends_with('/')
|| relative_artifact_path(Path::new(""), Path::new(&self.source)).is_none()
{
bail!("brew-cask: invalid app source '{}'", self.source);
}
file_name_str(Path::new(&self.source), "app source")
}
}
impl BinaryArtifact {
fn target_name(&self) -> Result<String> {
match &self.target {
Some(target) => Ok(target.clone()),
None => Ok(file_name_str(Path::new(&self.source), "binary source")?.to_string()),
}
}
fn target_path(&self, appdir: &Path) -> Result<PathBuf> {
binary_target_path(&self.target_name()?, appdir)
}
}
View on GitHub (pinned to 533346cc37)