BloopAI/vibe-kanban · error · FilesystemError
Operation forcibly terminated due to hard timeout
Error message
Operation forcibly terminated due to hard timeout
What it means
list_git_repos_with_timeout scans the filesystem for git repositories under a hard deadline. If the scan doesn't finish within hard_timeout_ms, the scan task is aborted and this TimedOut error is returned to prevent the API call from hanging on huge or slow filesystems.
Source
Thrown at crates/services/src/services/filesystem.rs:160
.await
});
let hard_timeout = tokio::time::sleep(std::time::Duration::from_millis(hard_timeout_ms));
tokio::pin!(hard_timeout);
tokio::select! {
res = &mut scan_handle => {
match res {
Ok(Ok(repos)) => Ok(repos),
Ok(Err(err)) => Err(err),
Err(join_err) => Err(FilesystemError::Io(
std::io::Error::other(join_err.to_string())))
}
}
_ = &mut hard_timeout => {
scan_handle.abort();
tracing::warn!("list_git_repos_with_timeout: hard timeout reached after {}ms", hard_timeout_ms);
Err(FilesystemError::Io(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Operation forcibly terminated due to hard timeout",
)))
}
}
}
#[cfg_attr(feature = "qa-mode", allow(unused_variables))]
pub async fn list_common_git_repos(
&self,
timeout_ms: u64,
hard_timeout_ms: u64,
max_depth: Option<usize>,
) -> Result<Vec<DirectoryEntry>, FilesystemError> {
#[cfg(feature = "qa-mode")]
{
tracing::info!(
"QA mode: returning hardcoded QA repos instead of scanning common directories"View on GitHub (pinned to 4deb7eca8f)
Solutions
- Scan a narrower root directory that excludes huge subtrees (build artifacts, node_modules).
- Increase the hard timeout configuration if your filesystem is legitimately slow (network mounts).
- Avoid scanning network-mounted paths; copy projects to local disk or mount without FUSE overhead.
- Retry later if transient I/O load caused the timeout; check the tracing warn log for the measured duration.
Example fix
// before
filesystem.list_git_repos(Path::new("/home/user"), None).await?;
// after
filesystem.list_git_repos(Path::new("/home/user/projects"), Some(hard_timeout_ms * 4)).await?; Defensive patterns
Strategy: fallback
Validate before calling
// pre-check: avoid scanning obviously huge trees
let entries = fs::read_dir(root)?.count();
if entries > 50_000 {
anyhow::bail!("root {} too large to scan", root.display());
} Try / catch
match list_git_repos(root).await {
Err(FilesystemError::Io(e)) if e.kind() == ErrorKind::TimedOut
&& e.to_string().contains("hard timeout") => {
// fall back to cached index or prompt user for a narrower directory
}
other => other,
} Prevention
- Point repo scans at dedicated project directories, not home or network mounts.
- Raise the hard timeout for known-slow (NFS/FUSE) filesystems.
- Cache scan results and invalidate on directory-change events instead of rescanning.
When it happens
Trigger: Calling list_git_repos or list_common_git_repos against a directory tree so large/slow (many directories, network mounts, spinning disk) that scanning exceeds the hard timeout.
Common situations: Pointing the directory scan at a home directory or NFS/SMB mount with thousands of directories; antivirus or FUSE filesystem slowdown; a path containing a huge node_modules/build tree; I/O contention from concurrent scans.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Copy project files timed out after 30s
- Approval has timed out
- Timeout: process took more than 30 seconds to start
- Copy files task failed: {e}
- File {source_file:?} is outside project directory
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/0ddf33b1a63897c5.
Report an issue: GitHub.