jdx/mise · error
invalid brew formula
Error message
invalid brew formula {name:?}; use brew:<formula> or brew:<owner>/<tap>/<formula> with normal, nonempty path components What it means
package_root validates brew formula names before deriving their Cellar path. Names must decompose into `brew:<formula>` or `brew:<owner>/<tap>/<formula>` with normal (non-empty, no whitespace/control chars, no ':' or backslash) path components; anything else is rejected to prevent malformed lookups and path traversal.
Solutions
- Use a plain formula name (brew:wget) or the full three-part form (brew:owner/tap/formula).
- Remove whitespace, control characters, ':', and backslashes from the name.
- Check for empty path segments (double slashes) in the spec.
- For nonstandard taps, register the tap with an explicit URL rather than encoding it into the formula string.
Example fix
// before let tool = "brew:homebrew-core/wget"; // 2 segments: invalid let tool2 = "brew:Cask/ thing"; // whitespace: invalid // after let tool = "brew:homebrew/core/wget"; // 3 normal segments let tool2 = "brew:wget"; // simple form
Defensive patterns
Strategy: validation
Validate before calling
fn valid_formula_spec(name: &str) -> bool {
let parts: Vec<&str> = name.split('/').collect();
matches!(parts.len(), 1 | 3)
&& parts.iter().all(|p| {
!p.is_empty()
&& !p.chars().any(|c| c.is_whitespace() || c.is_control() || matches!(c, ':' | '\\'))
&& !p.starts_with('.')
})
} Type guard
fn as_formula_spec(name: &str) -> Option<Vec<&str>> {
let parts: Vec<&str> = name.split('/').collect();
if matches!(parts.len(), 1 | 3)
&& parts.iter().all(|p| !p.is_empty() && !p.chars().any(|c| c.is_whitespace() || c.is_control() || matches!(c, ':' | '\\')))
{ Some(parts) } else { None }
} Prevention
- Validate tool specs when parsing config, before passing to the backend.
- Reject names containing ':', backslashes, or whitespace at the config layer.
- Use simple formula names or full owner/tap/formula — never mixed forms.
- Strip shell quoting artifacts before constructing brew specs.
When it happens
Trigger: Called from assert_query_target and package_root tests; triggered by names containing whitespace, control characters, ':', backslash, empty segments, or segments failing is_normal_formula_component (e.g. leading '.' or '-'), or more/fewer than 1 or 3 path segments.
Common situations: Copy-pasting formula specs with stray spaces or Windows-style backslashes; passing a full bottle URL or versioned spec as the name; empty segments from typos like "brew://wget"; trying to sneak path separators into a formula name.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- brew-cask: invalid structured flight path
- brew-cask: ' ' must stay below Applications
- app target is outside an allowed Applications directory
- artifact target has changed
- binary target is not an owned Caskroom symlink
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/8dfef6eb96a78d51.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/mod.rs:65
pub(crate) use cask::{
BrewCaskManager, apply_cask_prune_plan, cask_formula_dependencies, cask_prune_plan,
};
pub(crate) use maintenance::{apply_prune_plan, default_tap_url, linked_formulae, prune_plan};
/// Resolve a canonical formula name or owner/tap/name to its installed opt path using local records.
/// Qualified names identify the rack by their final component; aliases and tap provenance are not resolved.
pub(crate) fn package_root(name: &str) -> Result<PathBuf> {
let parts = name.split('/').collect::<Vec<_>>();
if !matches!(parts.len(), 1 | 3)
|| parts.iter().any(|part| {
part.is_empty()
|| part
.chars()
.any(|c| c.is_whitespace() || c.is_control() || matches!(c, ':' | '\\'))
|| !is_normal_formula_component(part)
})
{
bail!(
"invalid brew formula {name:?}; use brew:<formula> or brew:<owner>/<tap>/<formula> with normal, nonempty path components"
);
}
if parts.len() == 3 && parts[0] == "homebrew" && parts[1] == "cask" {
bail!("brew:{name}: the Homebrew cask namespace is unsupported for formula lookup");
}
let formula = request_formula_name(name);
assert!(
is_normal_formula_component(formula),
"validated formula must normalize to exactly one normal path component"
);
pour::strict_package_root(formula)
.wrap_err_with(|| format!("failed to locate installed brew:{name}"))
}
/// Require one normal path component so a formula name stays within its expected rack.
fn is_normal_formula_component(name: &str) -> bool {
let mut components = Path::new(name).components();View on GitHub (pinned to 533346cc37)