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

  1. Use a plain formula name (brew:wget) or the full three-part form (brew:owner/tap/formula).
  2. Remove whitespace, control characters, ':', and backslashes from the name.
  3. Check for empty path segments (double slashes) in the spec.
  4. 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

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


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)