jdx/mise · error

invalid recovery destination

Error message

invalid recovery destination

What it means

validate_destination rejects a recovery destination that is not an absolute path or contains a `..` (ParentDir) component. This is a safety check so the restore step can never write outside the intended location or depend on relative-path resolution. It indicates corrupt or malicious journal data rather than anything the user did at runtime.

Source

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

                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(())
}

fn validate_blob_id(hash: &str) -> Result<()> {
    if hash.len() != 64 || !hash.bytes().all(|b| b.is_ascii_hexdigit()) {
        bail!("invalid recovery content identifier");
    }
    Ok(())
}

pub(super) fn read_blob(state_dir: &Path, blob: &Blob) -> Result<Vec<u8>> {
    use base64::Engine;

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the pending journal in the state directory for entries with relative or `..` paths and correct or remove them.
  2. Delete the corrupt pending-operation record (accepting current file contents) if its journal cannot be trusted.
  3. Report it as a bug if no one edited the state directory: journal entries should always carry canonical absolute paths.

Example fix

// before (corrupt journal entry)
"path": "../home/user/.zshrc"

// after (canonical absolute path)
"path": "/home/user/.zshrc"
Defensive patterns

Strategy: validation

Validate before calling

// before trusting a journal, verify destinations
fn is_safe_destination(p: &std::path::Path) -> bool {
    p.is_absolute() && !p.components().any(|c| matches!(c, std::path::Component::ParentDir))
}

Try / catch

match result { Err(e) if e.to_string().contains("invalid recovery destination") => /* journal corrupt; discard record and restore manually */, other => other? }

Prevention

When it happens

Trigger: recover_path -> validate_destination receives a journal entry whose PathChanged path is relative or contains `..` components; typically only possible with a hand-edited or corrupted pending-operation journal in the state directory.

Common situations: A corrupted state directory, a journal file modified by hand or by an untrusted tool, or a bug in code that constructed journal entries with non-canonicalized paths.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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