jdx/mise · error
invalid or unenrolled permission path {path}
Error message
invalid or unenrolled permission path {path} What it means
Each entry in the manifest's permissions map must have mode bits no greater than 0o777, a path accepted by is_safe_branch_path, and the path must correspond to an enrolled stream via owns_stream. This error rejects permission entries that are malformed or reference paths not enrolled in the manifest.
Source
Thrown at src/system/history/manifest.rs:387
}
let mut variants = std::collections::BTreeSet::new();
super::select::validate(&entry.variants)?;
for variant in &entry.variants {
let name = variant.name();
if name.contains('@')
|| !super::sync::layout::is_safe_branch_path(&name)
|| !variants.insert(name)
{
bail!("invalid or repeated variant for {}", entry.path);
}
}
}
for (path, bits) in &self.permissions {
if *bits > 0o777
|| !super::sync::layout::is_safe_branch_path(path)
|| !self.owns_stream(path)
{
bail!("invalid or unenrolled permission path {path}");
}
}
Ok(())
}
pub(crate) fn read(repo: &HistoryRepo, tree: &str) -> Result<Option<Self>> {
let Some((mode, oid)) = repo.object_at(tree, PATH)? else {
return Ok(None);
};
if mode != "100644" {
bail!("dotfile enrollment metadata must be a regular file");
}
#[derive(Deserialize)]
struct FormatHeader {
format: u64,
}
let bytes = repo.cat_object_bounded(&oid, 4 * 1024 * 1024)?;
let header: FormatHeader = serde_json::from_slice(&bytes)?;View on GitHub (pinned to afd2eddd3a)
Solutions
- Ensure the permissions path exactly matches an enrolled stream path (enroll the stream first).
- Clamp permission bits to 0o777 (drop setuid/setgid/sticky bits).
- Fix the path so it passes is_safe_branch_path (no traversal, relative, safe chars).
- Remove the stale permissions entry if the stream was intentionally unenrolled.
Example fix
// before (manifest JSON)
{"permissions": {"dotfiles/vimrc": 0o4755}}
// after
{"permissions": {"dotfiles/vimrc": 0o755}} Defensive patterns
Strategy: validation
Validate before calling
let bits_ok = bits & !0o777 == 0;
let enrolled = manifest.streams.iter().any(|s| s.path == path);
if !(bits_ok && enrolled) { return Err(format!("bad permission entry: {path}")); } Type guard
fn valid_permission(path: &str, bits: u32) -> bool { bits <= 0o777 && !path.contains("..") } Try / catch
if let Err(e) = manifest.validate() {
if e.to_string().contains("invalid or unenrolled permission path") {
// enroll the stream or drop the permission entry
}
} Prevention
- Enroll a stream before adding permissions for its path.
- Mask permission bits with 0o777 before storing.
- Keep permission paths verbatim identical to enrolled stream paths.
When it happens
Trigger: Calling validate on a manifest where a permissions key has bits > 0o777, is an unsafe branch path, or names a path for which owns_stream returns false (no matching stream enrollment).
Common situations: Setting permission bits with extra flag bits (setuid/setgid/sticky) in the manifest; adding permissions for a dotfile before enrolling its stream; typos in the path so it doesn't match an enrolled stream.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- [dotfiles]."{}": manifest requires the source to be a direct
- invalid or repeated variant for {}
- dotfile enrollment metadata must be a regular file
- the packslip names an executable {:?}, which is not a plain
- a glob is required
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/4c4e59a7d904e325.
Report an issue: GitHub.