Hmbown/CodeWhale · error · io::Error

restore refuses directories, symlinks and non-regular files

Error message

restore refuses directories, symlinks and non-regular files

What it means

The second half of validate_restore_file checks the on-disk file type at the restore target (symlink_metadata, so symlinks are detected without following). A symlink, a directory where the workspace path is expected, or a non-regular/non-directory entry (fifo, socket, device) anywhere in the path is refused: restore only handles plain workspace files. NotFound is treated as "nothing to restore" (Ok(false)), not an error.

Solutions

  1. Replace the symlink/special file with a regular file (or remove it) before restoring.
  2. Remove the directory occupying the target path if a file should be restored there.
  3. Re-check workspace state (e.g. after a branch switch) and refresh the change record, then retry.
Defensive patterns

Strategy: validation

Validate before calling

fn restorable_target(ws: &Path, rel: &Path) -> bool {
    match std::fs::symlink_metadata(ws.join(rel)) {
        Ok(m) => m.is_file() && !m.file_type().is_symlink(),
        Err(_) => true, // NotFound is fine: nothing to overwrite
    }
}

Prevention

When it happens

Trigger: Restoring a path where the workspace target is a symlink, a directory (when restoring into the work tree), or a special file; or where a parent component is not a regular directory.

Common situations: Workspace switched branches and a formerly-regular file became a symlink; user replaced a file with a directory; project uses symlinked source files; leftover named pipes from build tooling.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/e498470d27538cee. Report an issue: GitHub.

Appendix: source

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

        {
            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,
                        "restore refuses directories, symlinks and non-regular files",
                    ));
                }
                Ok(_) => {}
                Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),
                Err(error) => return Err(error),
            }
        }
        Ok(true)
    }

    fn snapshot_contains_regular_file(&self, id: &SnapshotId, rel: &Path) -> io::Result<bool> {
        let entry = run_git(
            &self.git_dir,
            &self.work_tree,
            &[
                "--literal-pathspecs",

View on GitHub (pinned to 73e0f67d83)