{"record":{"id":"5dddffd5be217008","repo":"jdx/mise","slug":"brew-cask-refusing-to-replace-structured-symlink-5dddff","errorCode":null,"errorMessage":"brew-cask: refusing to replace structured symlink directory '{}'","messagePattern":"brew-cask: refusing to replace structured symlink directory '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/flight.rs","lineNumber":756,"sourceCode":"                create_flight_dir_all(&target, *sudo)?;\n                if created_external_directory || targets.previous_directories.contains(&target) {\n                    targets.record_installed_directory(target.clone());\n                }\n            }\n            for source in sources {\n                let link = if target_is_dir {\n                    let source_name = Path::new(&source).file_name().ok_or_else(|| {\n                        eyre!(\"brew-cask: structured symlink source has no file name\")\n                    })?;\n                    target.join(source_name)\n                } else {\n                    target.clone()\n                };\n                let external = !link.starts_with(staged_path);\n                let link_metadata = link.symlink_metadata().ok();\n                if let Some(metadata) = &link_metadata {\n                    if metadata.is_dir() {\n                        bail!(\n                            \"brew-cask: refusing to replace structured symlink directory '{}'\",\n                            link.display()\n                        );\n                    }\n                    if link.exists() && !force && !targets.previous_symlinks.contains(&link) {\n                        bail!(\n                            \"brew-cask: structured symlink target '{}' already exists\",\n                            link.display()\n                        );\n                    }\n                    if external {\n                        targets.protect(&link)?;\n                    } else if metadata.file_type().is_symlink() {\n                        file::remove_file(&link)?;\n                    } else {\n                        file::remove_all(&link)?;\n                    }\n                }","sourceCodeStart":738,"sourceCodeEnd":774,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/cask/flight.rs#L738-L774","documentation":"A brew-cask structured symlink step resolved an individual link path to an existing real directory. The library unconditionally refuses to replace a directory with a symlink, because doing so could destroy user data — this is a hard safety check with no force override.","triggerScenarios":"FlightStep::Symlink whose per-source link path (target, or target joined with the source's file name in multi-source mode) collides with an existing directory — e.g. a source file name matching an existing config/cache directory, or a target that is a real directory in single-source mode.","commonSituations":"Linking a CLI into a directory where a same-named directory exists (e.g. app support folders); a renamed upstream artifact now colliding with a real directory; linking into ~/Applications where a same-named .app directory exists as a real dir rather than a symlink.","solutions":["Move or delete the existing directory at the link path manually (after preserving any user data), then retry.","Change the link target to a path that does not collide with a real directory.","Rename the source artifact so the derived link name (target/<source file name>) no longer matches the directory."],"exampleFix":"// before\ntarget: \"~/Library/Application Support/MyApp\"  // exists as real dir\n// after\ntarget: \"~/bin/myapp\"  // non-directory location","handlingStrategy":"validation","validationCode":"let link = target.join(source_file_name);\nif link.symlink_metadata().map(|m| m.is_dir()).unwrap_or(false) {\n    return Err(eyre!(\"refusing to replace real directory at {}\", link.display()));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Inspect the link destination for existing directories before defining the step.","Avoid source file names that collide with common directory names (config/cache dirs).","Remember there is no force override for real directories — resolve conflicts manually."],"tags":["brew-cask","symlink","safety-check","directory-conflict"],"backgroundTag":"file-already-exists","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"}