{"record":{"id":"348ad911987e2de4","repo":"jdx/mise","slug":"dotfile-enrollment-metadata-must-be-a-regular-file","errorCode":null,"errorMessage":"dotfile enrollment metadata must be a regular file","messagePattern":"dotfile enrollment metadata must be a regular file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/history/manifest.rs","lineNumber":398,"sourceCode":"            }\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        };\n        if mode != \"100644\" {\n            bail!(\"dotfile enrollment metadata must be a regular file\");\n        }\n        let bytes = repo.cat_object_bounded(&oid, 4 * 1024 * 1024)?;\n        Self::from_bytes(&bytes).map(Some)\n    }\n\n    pub(crate) fn read_gix(tree: &gix::Tree<'_>) -> Result<Option<Self>> {\n        let Some(entry) = tree.lookup_entry_by_path(PATH)? else {\n            return Ok(None);\n        };\n        if entry.mode().value() != 0o100644 {\n            bail!(\"dotfile enrollment metadata must be a regular file\");\n        }\n        if entry.id().header()?.size() > 4 * 1024 * 1024 {\n            bail!(\"dotfile enrollment metadata is too large\");\n        }\n        let object = entry.object()?;\n        Self::from_bytes(&object.data).map(Some)\n    }","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/history/manifest.rs#L380-L416","documentation":"The enrollment metadata object inside the repository tree must be stored as a regular blob with git mode 100644. `read` bails when the object exists at the manifest path but has a different mode (e.g. a symlink, submodule, or executable), because the manifest loader can only safely parse a regular file's contents.","triggerScenarios":"Calling `Manifest::read` against a tree where the manifest path (PATH) resolves to a non-100644 entry — e.g. the metadata file was committed as executable (100755), replaced by a symlink (120000), or turned into a gitlink (160000/submodule).","commonSituations":"Someone `chmod +x`'d the manifest file before committing; the manifest path was replaced by a symlink; a merge or import tool committed the metadata with the wrong mode; CI writes the manifest via a script that changes its mode.","solutions":["Fix the git mode of the metadata file to 100644 and commit: `git add --chmod=644 <manifest-path>`","Remove any symlink/submodule at the manifest path and replace it with a regular file containing the manifest contents","Inspect the tree entry mode (`git ls-tree <tree> <path>`) to confirm what object occupies the manifest path, then repair it"],"exampleFix":"// before\n$ git ls-tree HEAD .history-manifest\n100755 blob ... .history-manifest\n// after\ngit add --chmod=644 .history-manifest && git commit -m \"fix manifest mode\"","handlingStrategy":"validation","validationCode":"let (mode, _oid) = repo.object_at(tree, PATH)\n  .ok_or(\"manifest missing\")?;\nif mode != \"100644\" { return Err(format!(\"manifest mode is {mode}, want 100644\")); }","typeGuard":null,"tryCatchPattern":"match Manifest::read(&repo, &tree) {\n  Err(e) if e.to_string() == \"dotfile enrollment metadata must be a regular file\" => {\n    // repair git mode to 100644 and retry\n  }\n  other => other,\n}","preventionTips":["Never chmod +x the metadata file or replace it with a symlink before committing","Check tree entry modes in CI (`git ls-tree`) for the manifest path","Commit metadata with explicit `git add --chmod=644` in scripts that generate it"],"tags":["git","manifest","file-mode","validation"],"backgroundTag":"unexpected-response-shape","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"}