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
- Restore only plain relative file paths inside the workspace
- Remove '..' or absolute components from the restore path
- 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(¤t_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)