jdx/mise · error

directory contents cannot be verified safely; left untouched

Error message

directory contents cannot be verified safely; left untouched

What it means

When the recorded 'after' state is a non-empty directory (or the prior state was not itself a directory), entry counts alone cannot prove the directory's identity, so recovery refuses to act to avoid clobbering unknown contents. It bails before restore, also after re-checking that the path still matches the 'after' state.

Source

Thrown at src/system/history/recovery.rs:100

    let Some(after) = after else {
        bail!("write completion was not recorded; inspect the live file before retrying recovery");
    };
    if PathState::observe(path) != *after {
        bail!("changed after the operation; left untouched");
    }
    // Entry count alone cannot establish a directory's identity. Never
    // replace a populated directory on that evidence.
    if matches!(after, PathState::Dir { entries, .. } if *entries != 0)
        && !(matches!(prior, PathSnapshot::Directory { .. })
            && matches!(
                after,
                PathState::Dir {
                    identity: Some(_),
                    ..
                }
            ))
    {
        bail!("directory contents cannot be verified safely; left untouched");
    }
    validate_snapshot(state_dir, prior)?;
    if PathState::observe(path) != *after {
        bail!("changed while preparing recovery; left untouched");
    }
    restore(state_dir, path, prior)
}

fn validate_destination(path: &Path) -> Result<()> {
    if !path.is_absolute() || path.components().any(|c| matches!(c, Component::ParentDir)) {
        bail!("invalid recovery destination");
    }
    for parent in path.ancestors().skip(1) {
        if std::fs::symlink_metadata(parent).is_ok_and(|meta| meta.is_symlink()) {
            bail!("a parent directory is now a symlink; left untouched");
        }
    }
    Ok(())

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Manually inspect the directory contents; remove or move files you do not need, then retry recovery.
  2. Use `recover <operation> --keep-current` to explicitly accept the directory's current contents.
  3. Recreate the directory structure via re-running bootstrap/enrollment instead of automatic restore.
  4. Restore the directory from backup if its contents are wrong.

Example fix

// before: populated dir blocks auto-restore
$ mise bootstrap dotfiles recover
// after: review the dir, then accept current contents
$ ls ~/.config/tool/
$ mise bootstrap dotfiles recover <operation> --keep-current
Defensive patterns

Strategy: validation

Validate before calling

// before recovering, ensure the directory is reviewable
let populated = std::fs::read_dir(path).map(|d| d.count() > 0).unwrap_or(false);
if populated { eprintln!("review contents; consider --keep-current"); }

Try / catch

match recover_entries(state_dir, plan) {
    Err(e) if e.to_string().contains("cannot be verified safely") => {
        eprintln!("{e}\ninspect the directory manually, then use --keep-current");
    }
    other => other?,
}

Prevention

When it happens

Trigger: recover_path invoked where `after` is PathState::Dir with entries != 0 and it is not the case that both `prior` and the observed state were identity-verified directories — i.e. a populated directory whose contents cannot be validated against the snapshot.

Common situations: Recovering an interrupted operation that replaced a populated directory (e.g. a config folder) where the crash left no verifiable identity; user files added inside the directory since the operation; restoring directories whose entries were never fully snapshotted.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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