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

  1. Scan a narrower root directory that excludes huge subtrees (build artifacts, node_modules).
  2. Increase the hard timeout configuration if your filesystem is legitimately slow (network mounts).
  3. Avoid scanning network-mounted paths; copy projects to local disk or mount without FUSE overhead.
  4. 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

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

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/0ddf33b1a63897c5. Report an issue: GitHub.