Hmbown/CodeWhale · error · std::io::Error

refusing to restore unsafe path

Error message

refusing to restore unsafe path '{rel}': restore requires a regular workspace file

What it means

Path-validation guard in validate_restore_file, the entry check for snapshot restore (also used by path_differs_from_snapshot, restore_file_if_unchanged, restore_paths_checked). It fires when the relative restore path fails is_safe_relative_path — traversal components, absolute paths, or other unsafe shapes. File restore never traverses symlinks, directories, or Git metadata, so any path that is not a plain relative workspace file path is rejected before any read, backup, or write happens.

Solutions

  1. Restore only plain relative file paths inside the workspace
  2. Remove '..' or absolute components from the restore path
  3. Restore directories or links through git tooling directly, not the file-restore path
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/tui/src/snapshot/repo.rs:639 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/78ddb9b67a9d19fc. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/snapshot/repo.rs:639

                "git checkout failed: {}",
                String::from_utf8_lossy(&checkout.stderr).trim()
            )));
        }
        self.remove_paths_missing_from_target(&current_paths, &target_paths)?;
        Ok(())
    }

    /// File restore never traverses symlinks, directories, or Git metadata.
    /// Validate every existing component before reading, backing up or writing.
    pub fn validate_restore_file(&self, rel: &Path) -> io::Result<bool> {
        if !is_safe_relative_path(rel)
            || rel.components().any(|part| {
                part.as_os_str()
                    .as_encoded_bytes()
                    .eq_ignore_ascii_case(b".git")
            })
        {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!(
                    "refusing to restore unsafe path '{}': restore requires a regular workspace file",
                    rel.display()
                ),
            ));
        }
        let mut path = self.work_tree.clone();
        for part in rel.components() {
            path.push(part);
            match std::fs::symlink_metadata(&path) {
                Ok(meta)
                    if meta.file_type().is_symlink()
                        || (path == self.work_tree.join(rel) && !meta.is_file())
                        || (path != self.work_tree.join(rel) && !meta.is_dir()) =>
                {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,

View on GitHub (pinned to 433685b202)