jdx/mise · error
changed during application; left untouched
Error message
{} changed during application; left untouched What it means
After writing a directory permission step and committing the journal, verify_written re-observes the directory and requires it to match the `written` snapshot recorded during application. If it doesn't match, something changed the directory while (or right after) mise was applying it, so mise reports the failure and leaves the path untouched rather than claiming success. This is the post-write counterpart of the planning-time validation check.
Solutions
- Re-run `mise dot sync`; a new plan will apply permissions against the current state.
- Pause concurrent mutators (file-sync daemons, dotfile watchers) while running sync.
- Check who changed the mode: compare `stat` output before/after and look for daemons touching the directory.
Defensive patterns
Strategy: retry
Validate before calling
before=$(stat -c '%d:%i:%a' ~/.config/foo); mise dot sync >/dev/null; after=$(stat -c '%d:%i:%a' ~/.config/foo); [ "$before" = "$after" ] || echo 'external mutator detected'
Try / catch
try_sync_with_retry() { mise dot sync || { sleep 2; mise dot sync; } } Prevention
- Disable file-sync daemons (Dropbox/Syncthing) or mode-changing watchers on synced trees during pulls.
- Avoid concurrent chmod automation on the same directories.
- Investigate any daemon that resets permissions (they often fight sync).
When it happens
Trigger: `Step::verify_written` (called from `recover` / the apply finalization loop) sees observe(path) != step.written — the directory's dev/ino/mode changed after the permission write, e.g. a concurrent chmod, or the directory was replaced between the write and the verification read.
Common situations: A script or editor daemon chmods dotfile directories during sync; two machines/processes sync against the same working tree; a watcher (direnv, file sync service like Dropbox) mutates modes mid-apply.
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
- changed after permission planning; retry pull
- changed during recovery; left untouched
- another setup sync or pull is running; retry shortly
- brew-cask: temporary artifact directory is not private
- cannot check staged changes in
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/48dbf7fe7611e442.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/sync/directories.rs:129
pub(super) fn apply(&mut self) -> Result<()> {
self.validate()?;
let pending = journal::begin_changes_with(
"history",
"directory permissions",
[(self.path.clone(), journal::Capture::Shallow)],
)?;
if self.before.is_none() {
crate::file::create_dir_all(&self.path)?;
}
set_mode(&self.path, self.desired)?;
self.written = observe(&self.path)?;
journal::commit_changes(pending);
Ok(())
}
pub(super) fn verify_written(&self) -> Result<()> {
if observe(&self.path)? != self.written {
bail!(
"{} changed during application; left untouched",
crate::file::display_path(&self.path)
);
}
Ok(())
}
pub(super) fn recover(&self) -> Result<()> {
if self.written.is_none() {
return Ok(());
}
self.verify_written()?;
match self.before {
Some((_, _, mode)) => set_mode(&self.path, mode),
None => std::fs::remove_dir(&self.path).map_err(Into::into),
}
}
}View on GitHub (pinned to 533346cc37)