{"record":{"id":"3fcd72bd3c5fdf3e","repo":"jdx/mise","slug":"brew-cask-command-wrapper-requires-content-or-exe","errorCode":null,"errorMessage":"brew-cask: command_wrapper requires content or executable","messagePattern":"brew-cask: command_wrapper requires content or executable","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":5153,"sourceCode":"        .collect::<Vec<_>>();\n    unsupported.sort();\n    if !unsupported.is_empty() {\n        bail!(\n            \"brew-cask: command_wrapper has unsupported option {}\",\n            unsupported.join(\", \")\n        );\n    }\n    let content = options\n        .get(\"content\")\n        .and_then(Value::as_str)\n        .map(str::to_string);\n    let executable = options\n        .get(\"executable\")\n        .and_then(Value::as_str)\n        .map(str::to_string);\n    match (content.is_some(), executable.is_some()) {\n        (false, false) => {\n            bail!(\"brew-cask: command_wrapper requires content or executable\")\n        }\n        (true, true) => {\n            bail!(\"brew-cask: command_wrapper requires content or executable, not both\")\n        }\n        _ => {}\n    }\n    let args = options\n        .get(\"args\")\n        .map(|args| {\n            args.as_array()\n                .ok_or_else(|| eyre!(\"brew-cask: command_wrapper args must be an array\"))?\n                .iter()\n                .map(|arg| {\n                    arg.as_str()\n                        .map(str::to_string)\n                        .ok_or_else(|| eyre!(\"brew-cask: command_wrapper args must be strings\"))\n                })\n                .collect::<Result<Vec<_>>>()","sourceCodeStart":5135,"sourceCodeEnd":5171,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L5135-L5171","documentation":"A command_wrapper must be materialized from either literal 'content' (a script body, with $HOMEBREW_PREFIX expansion) or an 'executable' (which mise wraps in a bash exec script). If the options object sets neither, mise has nothing to write and bails immediately after parsing the two fields.","triggerScenarios":"Wrapper metadata like [\"tool\", {\"args\": [...]}] or [\"tool\", {}] — options present but both content and executable absent, hitting the (false, false) arm of the match at src/system/packages/brew/cask.rs:5157.","commonSituations":"Template-generated metadata where the content field failed to render; authors assuming the wrapper wraps the cask's app binary by default; YAML indentation putting the body under the wrong key.","solutions":["Add 'executable' pointing at a binary inside the staged payload (most common), e.g. {\"executable\": \"MyApp.app/Contents/MacOS/tool\"}.","Or add 'content' with the full script body (use $HOMEBREW_PREFIX for prefix-relative paths).","Lint generated metadata to require one of the two keys per wrapper."],"exampleFix":"# before\ncommand_wrapper: [[\"tool\", {\"args\": [\"--fast\"]}]]\n\n# after\ncommand_wrapper: [[\"tool\", {\"executable\": \"MyApp.app/Contents/MacOS/tool\", \"args\": [\"--fast\"]}]]","handlingStrategy":"validation","validationCode":"jq -e '.command_wrapper[]? | .[1] | has(\"content\") or has(\"executable\")' cask.json >/dev/null || echo 'wrapper needs content or executable'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default to 'executable' when wrapping a payload binary; use 'content' only for custom scripts.","Schema-require one of the two keys per wrapper entry.","Test-render templates so content fields can never come out empty."],"tags":["brew-cask","command-wrapper","required-field","mise"],"backgroundTag":"missing-required-argument","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}