jdx/mise · error

{} has unsaved directory permission changes; save them befor

Error message

{} has unsaved directory permission changes; save them before pulling. Sharing is paused

What it means

During directory permission planning, if a directory currently has permission bits different from those saved in history, the planner treats the change as unsaved local work. Pulling would clobber it, so the operation aborts and sharing is paused until the user saves (commits) the permission change.

Source

Thrown at src/system/history/sync/directories.rs:82

        let path = roots.locate(portable).path().unwrap().to_path_buf();
        let before = observe(&path)?;
        let desired = tracked
            .manifest
            .permissions
            .get(portable)
            .copied()
            .unwrap_or(0o755);
        let was_directory = local
            .as_deref()
            .map(|head| repo.object_at(head, portable))
            .transpose()?
            .flatten()
            .is_some_and(|(mode, _)| mode == "040000");
        if was_directory
            && before.map(|(_, _, bits)| bits)
                != Some(saved.permissions.get(portable).copied().unwrap_or(0o755))
        {
            bail!(
                "{} has unsaved directory permission changes; save them before pulling. Sharing is paused",
                crate::file::display_path(&path)
            );
        }
        if before.map(|(_, _, bits)| bits) != Some(desired) {
            steps.push(Step {
                path,
                before,
                desired,
                written: None,
            });
        }
    }
    steps.sort_by_key(|step| step.path.components().count());
    Ok(steps)
}

impl Step {

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Save the current permission change into setup history (commit it), then re-run the pull.
  2. Revert the chmod back to the recorded mode (`chmod 755 <dir>` unless a different mode was saved).
  3. Re-run pull after reconciling; sharing resumes automatically.

Example fix

// before
chmod 700 ~/.config/app   # unsaved
// after
chmod 700 ~/.config/app && mise history save   # record before pulling
Defensive patterns

Strategy: validation

Validate before calling

let bits = dir_mode(&path)?;
let saved = saved_permissions.get(&path).copied().unwrap_or(0o755);
if bits != saved {
    // commit/revert the permission change before pulling
}

Prevention

When it happens

Trigger: Directory mode on disk differs from the saved permission entry (default 0o755 when nothing saved) for a tracked directory at plan time.

Common situations: A user ran chmod on a config directory without recording it in setup history; a package manager or tool tightened/loosened directory perms; restoring from backup with different umask.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/4b3b914d3a900e3e. Report an issue: GitHub.