{"record":{"id":"fc34c653f1e6404a","repo":"jdx/mise","slug":"duplicate-dock-application","errorCode":null,"errorMessage":"duplicate Dock application: {}","messagePattern":"duplicate Dock application: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/defaults/dock.rs","lineNumber":36,"sourceCode":"            eyre::ensure!(\n                !Path::new(rest).has_root(),\n                \"[bootstrap.macos.dock].apps: expected a path relative to home: {value}\"\n            );\n            crate::env::HOME.join(rest)\n        } else {\n            PathBuf::from(value)\n        };\n        eyre::ensure!(\n            path.is_absolute()\n                && path\n                    .extension()\n                    .is_some_and(|ext| ext.eq_ignore_ascii_case(\"app\"))\n                && !path.components().any(|c| matches!(c, Component::ParentDir))\n                && !value.contains('\\0'),\n            \"[bootstrap.macos.dock].apps: expected an absolute or ~/ application path: {value}\"\n        );\n        let path: PathBuf = path.components().collect();\n        eyre::ensure!(\n            seen.insert(crate::file::canonicalize_or_self(&path)),\n            \"duplicate Dock application: {}\",\n            path.display()\n        );\n        paths.push(path);\n    }\n    Ok(paths)\n}\n\nfn app_path(tile: &plist::Value) -> Option<PathBuf> {\n    let tile = tile.as_dictionary()?;\n    if tile.get(\"tile-type\")?.as_string()? != \"file-tile\" {\n        return None;\n    }\n    let location = tile\n        .get(\"tile-data\")?\n        .as_dictionary()?\n        .get(\"file-data\")?","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/defaults/dock.rs#L18-L54","documentation":"Thrown by dock::paths when two entries in [bootstrap.macos.dock].apps resolve to the same Dock application. Each path is normalized (components collected, canonicalized via canonicalize_or_self) and inserted into a set; a duplicate insert fails, since patching the same app twice into the Dock would be ambiguous.","triggerScenarios":"The same .app listed twice in dock.apps, or two paths that canonicalize identically (e.g. \"/Applications/Foo.app\" and \"/Applications/./Foo.app\", or a symlink resolving to an already-listed app).","commonSituations":"Listing an app via both a symlink (e.g. ~/Applications alias) and its real location; merging config layers that each add the same app; copy-paste duplicates in the apps array.","solutions":["Remove the duplicate entry from the apps array","If one path is a symlink to another listed app, keep only the canonical path","Deduplicate merged config layers so each app appears once"],"exampleFix":"// before\napps = [\"/Applications/Safari.app\", \"/Applications/./Safari.app\"]\n// after\napps = [\"/Applications/Safari.app\"]","handlingStrategy":"validation","validationCode":"fn validate_no_dock_duplicates(values: &[String]) -> Result<(), String> {\n    let mut seen = std::collections::HashSet::new();\n    for v in values {\n        let p: std::path::PathBuf = Path::new(v).components().collect();\n        let key = std::fs::canonicalize(&p).unwrap_or_else(|_| p.clone());\n        if !seen.insert(key) {\n            return Err(format!(\"duplicate Dock application: {v}\"));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Deduplicate the apps array (on canonical paths) before applying","Avoid listing both a symlink and its target for the same app","When merging config layers, union dock apps by canonical path"],"tags":["macos","dock","validation","duplicate"],"backgroundTag":"file-already-exists","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"}