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 = optionsView on GitHub (pinned to 6f52dcdf99)
Solutions
- Use a plain command name: 'tool', not 'bin/tool'.
- If the wrapper must exec a specific path, put the path in the 'executable' option and keep the name bare: ["tool", {"executable": "bin/tool"}].
- 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
- Keep wrapper names single path components; put real paths in the 'executable' option.
- Regex-lint names in metadata: /^[^/]+$/ plus not '.'/'..'.
- Generate wrappers from a template that takes name and executable as separate fields.
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
- brew-cask: command_wrapper '{}' must set exactly one of cont
- brew-cask: command wrapper '{}' was not staged
- brew-cask: command_wrapper has unsupported option {}
- brew-cask: command_wrapper requires content or executable
- brew-cask: command_wrapper requires content or executable, n
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/3ab367f5821b2a04.
Report an issue: GitHub.