{"record":{"id":"cc6932cccb1d1de3","repo":"jdx/mise","slug":"brew-cask-structured-copy-source-must-resolve-to-cc6932","errorCode":null,"errorMessage":"brew-cask: structured copy source must resolve to exactly one path","messagePattern":"brew-cask: structured copy source must resolve to exactly one path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/flight.rs","lineNumber":651,"sourceCode":"                        file::remove_file_or_dir(&path)?;\n                    }\n                }\n            }\n        }\n        FlightStep::Copy {\n            source,\n            target,\n            recursive,\n            overwrite,\n            source_glob,\n            guards,\n        } => {\n            if !flight_guards_pass(cask, guards, staged_path, appdir)? {\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()","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/flight.rs#L633-L669","documentation":"During a brew-cask structured copy flight step, the source selector is expected to resolve to exactly one path. When `source_glob` is enabled, the glob is expanded and this error fires if it matches zero or more than one path, because a copy step copies a single artifact onto a single target path. This is a cask-definition sanity check to avoid ambiguous copies.","triggerScenarios":"A FlightStep::Copy with source_glob=true whose pattern matches 0 or >=2 files inside staged_path (e.g. \"*.app\" matching two bundles, or a typo matching nothing). A non-glob Copy never hits this branch.","commonSituations":"Cask authors writing glob-based copy steps after an upstream app rename added a second matching bundle; version changes altering staged contents so a formerly unique glob now matches multiple files or none; copy-pasting a Symlink-style multi-match glob into a Copy step.","solutions":["Tighten the copy source glob so it matches exactly one path (add distinguishing segments, e.g. \" MyApp.app\" instead of \"*.app\").","If multiple matches are intended, replace the single Copy step with a Symlink step (which supports directory targets and multiple sources) or several Copy steps.","If the glob matched nothing, verify the file exists in the staged path at this install step (it may only appear after a prior Move/Copy step)."],"exampleFix":"// before\nFlightStep::Copy { source: glob(\"**/*.app\"), source_glob: true, .. }\n// after\nFlightStep::Copy { source: path(\"MyApp.app\"), source_glob: false, .. }","handlingStrategy":"validation","validationCode":"let sources = flight_symlink_sources(cask, source, true, staged_path, appdir)?;\nif sources.len() != 1 {\n    return Err(eyre!(\"copy source glob must match exactly one path, got {}\", sources.len()));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer literal paths for Copy steps; reserve globs for Symlink steps.","Test the glob against the exact staged tree of each supported version.","Keep glob patterns specific enough to match one artifact."],"tags":["brew-cask","glob","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"}