{"record":{"id":"bfb3e16d2a4640ad","repo":"jdx/mise","slug":"brew-cask-git-only-path-must-name-a-directory","errorCode":null,"errorMessage":"brew-cask:{}: git only_path must name a directory within the checkout","messagePattern":"brew-cask:(.+?): git only_path must name a directory within the checkout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/fetch.rs","lineNumber":223,"sourceCode":"        || only_path\n            .components()\n            .any(|component| matches!(component, Component::ParentDir | Component::Prefix(_)))\n    {\n        bail!(\n            \"brew-cask:{}: git only_path must stay within the checkout\",\n            cask.token\n        );\n    }\n    let clone_root = clone_dir.canonicalize()?;\n    let source = clone_dir.join(only_path).canonicalize().wrap_err_with(|| {\n        format!(\n            \"brew-cask:{}: git only_path does not exist: {}\",\n            cask.token,\n            only_path.display()\n        )\n    })?;\n    if !source.starts_with(&clone_root) || !source.is_dir() {\n        bail!(\n            \"brew-cask:{}: git only_path must name a directory within the checkout\",\n            cask.token\n        );\n    }\n    Ok(source)\n}\n\npub(super) async fn fetch_archive(cask: &Cask, pr: Option<&dyn SingleReport>) -> Result<PathBuf> {\n    let filename = archive_filename(&cask.url)\n        .ok_or_else(|| eyre!(\"brew-cask:{}: URL has no file name\", cask.token))?;\n    let cache_dir = crate::dirs::CACHE.join(\"system-brew\").join(\"casks\");\n    file::create_dir_all(&cache_dir)?;\n    let url_hash = &hash::hash_sha256_to_str(&cask.url)[..12];\n    let archive = cache_dir.join(format!(\n        \"{}-{}-{url_hash}-{filename}\",\n        cask.token, cask.version\n    ));\n    if !archive.exists() {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/fetch.rs#L205-L241","documentation":"After validating only_path syntax, `git_only_path_source` canonicalizes clone_dir.join(only_path) and requires that the result is inside the canonical clone root AND is a directory. If the resolved path escapes the checkout (e.g. via symlinks) or points at a file rather than a directory, this error is thrown because only staging a directory subtree is supported.","triggerScenarios":"fetch_git_clone_and_stage passes an only_path that canonicalizes outside the clone root (symlink escape) or resolves to a regular file instead of a directory within the cloned repository.","commonSituations":"A cask's only_path references a single file, not a directory; the repository restructured and the old subdirectory no longer exists or was replaced by a symlink; typo in the subdirectory name.","solutions":["Point only_path at a directory that exists in the current repository layout","If you need a single file, stage its containing directory instead","Update the cask stanza after upstream repository restructuring"],"exampleFix":"// before\ncask \"repo/x\", only_path: \"bin/tool\"\n// after\ncask \"repo/x\", only_path: \"share/tool\"","handlingStrategy":"validation","validationCode":"// Confirm the target is a directory inside the checkout\nlet root = std::fs::canonicalize(clone_dir)?;\nlet src = std::fs::canonicalize(clone_dir.join(only_path))?;\nif !src.starts_with(&root) || !src.is_dir() {\n    eprintln!(\"only_path must resolve to a directory within the checkout\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify the subdirectory still exists after upstream repo restructuring","Point only_path at directories, not individual files","Avoid stanzas relying on symlinks that leave the checkout"],"tags":["homebrew","cask","git","path-validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}