jdx/mise · error
brew-cask: command_wrapper args and env require executable
Error message
brew-cask: command_wrapper args and env require executable
What it means
Thrown by parse_command_wrapper_artifact in mise's brew-cask parser. A command_wrapper artifact options object must describe EITHER an inline script ('content') OR a wrapped 'executable'; 'args' and 'env' only make sense when an executable is being wrapped. Earlier checks (cask.rs:5151-5158) already guarantee content and executable are mutually exclusive, so reaching this branch means content was chosen AND args/env were also supplied, which has nothing to apply to.
Source
Thrown at src/system/packages/brew/cask.rs:5197
.ok_or_else(|| eyre!("brew-cask: command_wrapper env must be an object"))?
.iter()
.map(|(key, value)| {
if !is_shell_env_name(key) {
bail!("brew-cask: invalid command_wrapper environment name '{key}'");
}
value
.as_str()
.map(|value| (key.clone(), value.to_string()))
.ok_or_else(|| {
eyre!("brew-cask: command_wrapper environment values must be strings")
})
})
.collect::<Result<BTreeMap<_, _>>>()
})
.transpose()?
.unwrap_or_default();
if content.is_some() && (!args.is_empty() || !env.is_empty()) {
bail!("brew-cask: command_wrapper args and env require executable");
}
Ok(Some(CommandWrapperArtifact {
name: name.to_string(),
target: artifact_target(value, values),
content,
executable,
args,
env,
}))
}
fn parse_pkg_artifact(value: &Value) -> Result<Option<PkgArtifact>> {
let Some(pkg) = value.as_object().and_then(|o| o.get("pkg")) else {
return Ok(None);
};
match pkg {
Value::String(source) => Ok(Some(PkgArtifact {
source: source.clone(),View on GitHub (pinned to 6f52dcdf99)
Solutions
- Remove "args" and "env" from the content-based wrapper and bake those flags into the script text itself
- Or switch to the executable form: {"command_wrapper": ["tool", {"executable": "real-binary", "args": [...], "env": {...}}]}
- If the JSON comes from Homebrew's API and you cannot edit it, update mise to a newer build and report the cask token upstream
Example fix
// before
{"command_wrapper": ["kubectl", {"content": "#!/bin/sh\nexec /target/wrapped kubectl \"$@\"", "args": ["--kubeconfig", "$HOME/.kube/config"]}]}
// after
{"command_wrapper": ["kubectl", {"content": "#!/bin/sh\nexec /target/wrapped kubectl --kubeconfig \"$HOME/.kube/config\" \"$@\""}]} Defensive patterns
Strategy: validation
Validate before calling
fn command_wrapper_ok(v: &serde_json::Value) -> bool {
let Some(opts) = v.get("command_wrapper").and_then(|w| w.get(1)).and_then(|o| o.as_object()) else { return true; };
let has_content = opts.get("content").map(|c| c.as_str().is_some()).unwrap_or(false);
let args_empty = opts.get("args").and_then(|a| a.as_array()).map(|a| a.is_empty()).unwrap_or(true);
let env_empty = opts.get("env").and_then(|e| e.as_object()).map(|e| e.is_empty()).unwrap_or(true);
!has_content || (args_empty && env_empty)
} Type guard
fn is_command_wrapper_options(v: &serde_json::Value) -> bool {
v.as_object().map(|o| {
(o.contains_key("content") ^ o.contains_key("executable"))
&& (o.get("args").map(|a| a.is_array()).unwrap_or(true))
&& (o.get("env").map(|e| e.is_object()).unwrap_or(true))
}).unwrap_or(false)
} Try / catch
match cask_artifacts(&cask) {
Ok(artifacts) => { /* proceed */ }
Err(e) if e.to_string().contains("command_wrapper") => {
eprintln!("cask {} has an invalid command_wrapper stanza; fix args/env vs content/executable: {e}", cask.token);
}
Err(e) => return Err(e),
} Prevention
- Treat command_wrapper as two exclusive shapes: {content} OR {executable, args, env} — never mix
- When generating cask JSON, emit args/env only when the executable key is present
- Run cask JSON through the parser in CI before shipping custom casks
When it happens
Trigger: A cask artifact like {"command_wrapper": ["tool", {"content": "#!/bin/sh ...", "args": ["--fast"]}]} or with a non-empty "env" map. Fires during cask_artifacts() parsing when mise installs or upgrades that cask; any non-empty args array or env object alongside content triggers it.
Common situations: Hand-written or shim-converted cask JSON (cask_shim.rb) that copied args/env from an executable-style wrapper into a content-style wrapper; metadata generators that emit every option unconditionally; custom internal casks.
Related errors
- brew-cask: command_wrapper '{}' must set exactly one of cont
- brew-cask: command wrapper '{}' was not staged
- brew-cask: command_wrapper requires a command name without p
- brew-cask: command_wrapper has unsupported option {}
- brew-cask: command_wrapper requires content or executable
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/a831612bf0b32f97.
Report an issue: GitHub.