{"record":{"id":"00d670078d1193f8","repo":"jdx/mise","slug":"brew-cask-structured-move-with-multiple-sources-r-00d670","errorCode":null,"errorMessage":"brew-cask: structured move with multiple sources requires a directory target","messagePattern":"brew-cask: structured move with multiple sources requires a directory target","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/flight.rs","lineNumber":603,"sourceCode":"}\n\npub(super) fn execute_flight_step(\n    cask: &Cask,\n    step: &FlightStep,\n    staged_path: &Path,\n    appdir: &Path,\n    targets: &mut FlightTargetTransaction,\n) -> Result<()> {\n    match step {\n        FlightStep::Move {\n            source,\n            target,\n            source_glob,\n        } => {\n            let sources = flight_sources(staged_path, source, *source_glob)?;\n            let target = resolve_flight_path(staged_path, target)?;\n            if sources.len() > 1 && !target.is_dir() {\n                bail!(\n                    \"brew-cask: structured move with multiple sources requires a directory target\"\n                );\n            }\n            for source in sources {\n                let target = if target.is_dir() {\n                    target.join(source.file_name().ok_or_else(|| {\n                        eyre!(\n                            \"brew-cask: structured move source '{}' has no file name\",\n                            source.display()\n                        )\n                    })?)\n                } else {\n                    target.clone()\n                };\n                if let Some(parent) = target.parent()\n                    && !parent.as_os_str().is_empty()\n                {\n                    file::create_dir_all(parent)?;","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/flight.rs#L585-L621","documentation":"A cask flight plan's Move step can name a source (or glob) that expands to multiple staged items. Multiple sources can only be moved when the target is an existing directory (each source lands inside it); a file target cannot receive several sources. mise validates this up front and aborts the flight step rather than guessing an implicit merge.","triggerScenarios":"execute_flight_step processes FlightStep::Move where flight_sources() yields more than one path (e.g. a glob matched several files) while resolve_flight_path(target) does not point at an existing directory. Reached via execute_flight_steps_with_completion and flight rollback tests.","commonSituations":"A cask stanza uses a glob ('staged/*.app' or 'bin/*') whose target stanza forgot the directory form (e.g. target '/Applications' vs '/Applications/MyApp'); upstream packaging changed so a previously single file now matches multiple files; cask author typo in artifacts definition.","solutions":["Fix the cask's Move step so the target is the destination directory when the source is a glob (e.g. target the app dir, not a file path).","Narrow the source glob so it matches exactly one item if a single-file target is intended.","Create the target directory first (a preceding Mkdir-like step or in packaging) so target.is_dir() holds.","Update the cask for the new upstream layout if packaging changed from one artifact to many."],"exampleFix":"// before (glob source, file target)\n{\"move\": {\"source_glob\": true, \"source\": \"staged/*\", \"target\": \"/Applications/MyApp\"}}\n// after\n{\"move\": {\"source_glob\": true, \"source\": \"staged/*\", \"target\": \"/Applications\"}}","handlingStrategy":"validation","validationCode":"// cask authoring check: glob sources need a directory target\nif source.includes('*') && !fs.existsSync(target) { // or statSync(target).isDirectory()\n  throw new Error(`move target '${target}' must be a directory for glob source`);\n}","typeGuard":"function isDirectoryTarget(target) {\n  try { return require('fs').statSync(target).isDirectory(); } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Pair glob/multi-file Move sources with a directory target in cask artifacts.","Test cask flight plans against the real staged layout before publishing.","Re-validate casks when upstream packaging changes file counts."],"tags":["brew","cask-flight","move","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}