{"record":{"id":"f240c0b177532fb8","repo":"jdx/mise","slug":"brew-cask-structured-directory-copy-requires-recu","errorCode":null,"errorMessage":"brew-cask: structured directory copy requires recursive=true","messagePattern":"brew-cask: structured directory copy requires recursive=true","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask.rs","lineNumber":3446,"sourceCode":"                .map(|guard| flight_guard_matches(cask, guard, staged_path, appdir))\n                .collect::<Result<Vec<_>>>()?\n                .into_iter()\n                .all(|matches| matches)\n            {\n                return Ok(());\n            }\n            let sources = flight_symlink_sources(cask, source, *source_glob, staged_path, appdir)?;\n            let [source] = sources.as_slice() else {\n                bail!(\"brew-cask: structured copy source must resolve to exactly one path\");\n            };\n            if !source.exists() {\n                bail!(\n                    \"brew-cask: structured copy source '{}' was not found\",\n                    source.display()\n                );\n            }\n            if source.is_dir() && !recursive {\n                bail!(\"brew-cask: structured directory copy requires recursive=true\");\n            }\n            let target = resolve_flight_path_with_context(cask, target, staged_path, appdir)?;\n            let external = !target.starts_with(staged_path);\n            let target_metadata = target.symlink_metadata().ok();\n            if target_metadata.is_some() {\n                if !overwrite {\n                    bail!(\n                        \"brew-cask: structured copy target '{}' already exists\",\n                        target.display()\n                    );\n                }\n                if external {\n                    targets.protect(&target)?;\n                } else {\n                    file::remove_all(&target)?;\n                }\n            }\n            if let Some(parent) = target.parent() {","sourceCodeStart":3428,"sourceCodeEnd":3464,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/cask.rs#L3428-L3464","documentation":"A structured copy step found its source path on disk, but the source is a directory and the step was declared without recursive=true. mise only copies directories when the recursive flag is set, mirroring Homebrew's single-file copy default, and aborts instead of guessing.","triggerScenarios":"Calling mise install on a cask whose copy source resolves to a directory (any .app bundle, .appex, or plain folder in the staged tree) while the copy artifact lacks recursive=true; typically after a payload changed from a single file to a bundle directory.","commonSituations":"Upstream cask metadata regression that dropped the recursive flag; an archive repackaged so a formerly-flat file became a directory; macOS/Archive Utility re-packaging producing nested .app bundles during cask authoring.","solutions":["Confirm the source is really a directory: ls -ld on the path from the error message","If copying the whole directory is intended, add recursive=true to the copy artifact in the cask data (upstream fix)","If a single file was intended, correct the source path to point at the file inside the bundle","File an issue with homebrew-cask if the shipped metadata is missing the flag"],"exampleFix":"// before (cask artifact data)\ncopy { source: \"payload/MyApp.app\", recursive: false }\n// after\ncopy { source: \"payload/MyApp.app\", recursive: true }","handlingStrategy":"validation","validationCode":"STAGED=\"$(brew --caskroom)/<token>/<version>\"\ntest -d \"$STAGED/payload/MyApp.app\" && echo \"source is a directory: copy step needs recursive=true\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When authoring cask data, set recursive=true for any source that is or can become a bundle directory","Remember .app/.appex are directories on macOS, not files","Re-check copy flags after payload repackaging changes file/dir shapes"],"tags":["brew","cask","copy","directory","recursive","artifact"],"backgroundTag":"missing-required-flag","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}