gitbutlerapp/gitbutler · error
not found
Error message
{} not found What it means
Converts a notify (filesystem watcher) error into a backoff error. notify::ErrorKind::PathNotFound becomes a permanent backoff error with the message '<path> not found', so retries are skipped. Other IO/invalid-config errors are also permanent; everything else is transient.
Solutions
- Verify the watched path exists before setting up the watch (fs::metadata).
- Re-point the watch to the current valid project directory.
- If the path is expected to appear later, defer watch setup until it exists (this error is permanent and will not retry).
- Check whether a git operation (checkout/clean) removed the directory and re-establish the watch afterward.
Example fix
// before
watcher.watch(&project_path, Recursive)?;
// after
if project_path.exists() {
watcher.watch(&project_path, Recursive)?;
} Defensive patterns
Strategy: validation
Validate before calling
if !path.is_dir() {
return Err(anyhow!("cannot watch missing directory: {}", path.display()));
}
// now safe to set up the watch Prevention
- Always check the target path exists before installing a watcher.
- Re-create watchers after git operations that can replace directories.
- Treat PathNotFound as permanent — do not rely on backoff retries for it.
When it happens
Trigger: Setting up a watch (setup_watch_plan / setup_legacy_watch) on a directory that does not exist or was deleted before/while watching — e.g. the workspace/project directory moved or removed.
Common situations: Project folder deleted or renamed externally while the app runs; watching a nested path removed during a git operation (checkout/clean); passing a stale path after re-cloning.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- The path does not exist
- assignments_error: assignments_error.clone()…
- assignments_error.map(|err| serde_error::Error::new(&*err))
- BUG: we do not create or work with symlinks
- Can only open the GUI on directories
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/e2577313ab8e8641.
Report an issue: GitHub.
Appendix: source
Thrown at crates/gitbutler-filemonitor/src/file_monitor.rs:579
)
)
}
#[cfg(target_os = "windows")]
fn is_interesting_kind(kind: notify::EventKind) -> bool {
matches!(
kind,
notify::EventKind::Create(_) | notify::EventKind::Modify(_) | notify::EventKind::Remove(_)
)
}
fn into_backoff_err(
err: notify::Error,
path: &Path,
) -> backoff::Error<Box<dyn std::error::Error + Send + Sync + 'static>> {
match err.kind {
notify::ErrorKind::PathNotFound => backoff::Error::permanent(
anyhow!("{} not found", path.display()).into_boxed_dyn_error(),
),
notify::ErrorKind::Io(_) | notify::ErrorKind::InvalidConfig(_) => {
backoff::Error::permanent(anyhow::Error::from(err).into_boxed_dyn_error())
}
_ => backoff::Error::transient(anyhow::Error::from(err).into_boxed_dyn_error()),
}
}
fn backoff_err_to_anyhow(
err: backoff::Error<Box<dyn std::error::Error + Send + Sync + 'static>>,
) -> anyhow::Error {
anyhow::Error::from_boxed(Box::from(err.to_string()))
}
pub const LOCAL_REFS_DIR: &str = "refs/heads/";
pub const REMOTE_REFS_DIR: &str = "refs/remotes/";
pub const FETCH_HEAD: &str = "FETCH_HEAD";
pub const HEAD: &str = "HEAD";View on GitHub (pinned to 58e5313667)