jdx/mise · error

brew-cask: command_wrapper requires a command name without p

Error message

brew-cask: command_wrapper requires a command name without path components

What it means

A command_wrapper artifact is a two-element array: [name, options]. mise validates the name is a single path component — Path::file_name() of it must equal the whole string and it must not be '.' or '..' — because the name becomes both a filename in the caskroom and a command in the bin directory. Anything containing '/', '.', or '..' is rejected up front.

Source

Thrown at src/system/packages/brew/cask.rs:5125

    }
}

fn parse_command_wrapper_artifact(value: &Value) -> Result<Option<CommandWrapperArtifact>> {
    let Some(wrapper) = value.as_object().and_then(|o| o.get("command_wrapper")) else {
        return Ok(None);
    };
    let values = wrapper
        .as_array()
        .ok_or_else(|| eyre!("brew-cask: command_wrapper metadata must be an array"))?;
    let name = values
        .first()
        .and_then(Value::as_str)
        .ok_or_else(|| eyre!("brew-cask: command_wrapper requires a command name"))?;
    let name_path = Path::new(name);
    if name_path.file_name().and_then(|name| name.to_str()) != Some(name)
        || matches!(name, "." | "..")
    {
        bail!("brew-cask: command_wrapper requires a command name without path components");
    }
    let options = values
        .get(1)
        .and_then(Value::as_object)
        .ok_or_else(|| eyre!("brew-cask: command_wrapper requires options"))?;
    let mut unsupported = options
        .keys()
        .filter(|key| !matches!(key.as_str(), "content" | "executable" | "args" | "env"))
        .cloned()
        .collect::<Vec<_>>();
    unsupported.sort();
    if !unsupported.is_empty() {
        bail!(
            "brew-cask: command_wrapper has unsupported option {}",
            unsupported.join(", ")
        );
    }
    let content = options

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Use a plain command name: 'tool', not 'bin/tool'.
  2. If the wrapper must exec a specific path, put the path in the 'executable' option and keep the name bare: ["tool", {"executable": "bin/tool"}].
  3. If you need namespaced commands, create separate wrappers with distinct plain names.

Example fix

# before
command_wrapper: [["bin/tool", {"executable": "MyApp.app/Contents/MacOS/tool"}]]

# after
command_wrapper: [["tool", {"executable": "MyApp.app/Contents/MacOS/tool"}]]
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_plain_command_name(name: &str) -> bool {
    !name.is_empty()
        && std::path::Path::new(name).file_name().and_then(|n| n.to_str()) == Some(name)
        && !matches!(name, "." | "..")
}
// assert is_plain_command_name(&wrapper_name) before building the artifact

Type guard

function isPlainCommandName(name) {
  return /^[^/]+$/.test(name) && name !== "." && name !== ".." && name.length > 0;
}

Prevention

When it happens

Trigger: Metadata where the wrapper name is 'bin/tool', 'foo/bar', './tool', '.', or '..' — the check at src/system/packages/brew/cask.rs:5125 compares file_name() back to the full string and explicitly bans the two special components.

Common situations: Authors pasting a path into the name slot instead of the executable option; metadata generators emitting $APPDIR-prefixed names; attempts to make the wrapper land in a subdirectory of bin (unsupported).

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/3ab367f5821b2a04. Report an issue: GitHub.