{"record":{"id":"4a237cdd70d0ce5c","repo":"jdx/mise","slug":"invalid-or-repeated-variant-for","errorCode":null,"errorMessage":"invalid or repeated variant for {}","messagePattern":"invalid or repeated variant for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/manifest.rs","lineNumber":378,"sourceCode":"        let mut paths = std::collections::BTreeSet::new();\n        for entry in &self.enrollment {\n            if !super::sync::layout::is_safe_branch_path(&entry.path)\n                || !(entry.path.starts_with(\"home/\")\n                    || entry.path == \"config\"\n                    || entry.path.starts_with(\"config/\"))\n                || !paths.insert(&entry.path)\n            {\n                bail!(\"invalid or repeated enrollment path {}\", entry.path);\n            }\n            let mut variants = std::collections::BTreeSet::new();\n            super::select::validate(&entry.variants)?;\n            for variant in &entry.variants {\n                let name = variant.name();\n                if name.contains('@')\n                    || !super::sync::layout::is_safe_branch_path(&name)\n                    || !variants.insert(name)\n                {\n                    bail!(\"invalid or repeated variant for {}\", entry.path);\n                }\n            }\n        }\n        for (path, bits) in &self.permissions {\n            if *bits > 0o777\n                || !super::sync::layout::is_safe_branch_path(path)\n                || !self.owns_stream(path)\n            {\n                bail!(\"invalid or unenrolled permission path {path}\");\n            }\n        }\n        Ok(())\n    }\n\n    pub(crate) fn read(repo: &HistoryRepo, tree: &str) -> Result<Option<Self>> {\n        let Some((mode, oid)) = repo.object_at(tree, PATH)? else {\n            return Ok(None);\n        };","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/history/manifest.rs#L360-L396","documentation":"During dotfile enrollment manifest validation, each variant name must be safe and unique. This error fires when a variant name contains '@', is not a safe branch path per sync::layout::is_safe_branch_path, or duplicates a variant already seen for the same entry. It prevents ambiguous or unportable stream names from being persisted.","triggerScenarios":"Calling Manifest::validate (directly or via read/write/tracking) on a manifest whose entry.variants contains a name with '@', an unsafe path (e.g. '..', absolute, reserved components), or the same variant name twice for one entry.path.","commonSituations":"Hand-edited or tool-generated enrollment manifests with duplicate variants; names copied from refs like 'origin/main@1'; paths with traversal components or characters rejected by is_safe_branch_path.","solutions":["Remove duplicate variant entries for the affected path, keeping one.","Remove '@' from the variant name; '@' is reserved syntax.","Rename the variant to satisfy is_safe_branch_path (relative, no traversal, safe characters).","Regenerate the manifest with the enrollment tooling instead of hand-editing."],"exampleFix":"// before (manifest JSON)\n{\"path\": \"dotfiles/zshrc\", \"variants\": [\"main\", \"main\"]}\n// after\n{\"path\": \"dotfiles/zshrc\", \"variants\": [\"main\", \"laptop\"]}","handlingStrategy":"validation","validationCode":"fn variant_ok(name: &str, seen: &mut std::collections::HashSet<&str>) -> bool {\n    !name.contains('@') && seen.insert(name)\n}","typeGuard":"fn is_safe_variant(name: &str) -> bool { !name.contains('@') && !name.contains(\"..\") && !name.starts_with('/') }","tryCatchPattern":"match manifest.validate() {\n    Err(e) if e.to_string().starts_with(\"invalid or repeated variant\") => {\n        eprintln!(\"fix manifest variants: {e}\");\n    }\n    r => r?,\n}","preventionTips":["Generate manifests via the enrollment tooling, never by hand.","Keep variant names free of '@' and path-unsafe characters.","Deduplicate variants per path before writing."],"tags":["validation","manifest","dotfiles","git"],"backgroundTag":"schema-validation-failed","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"}