BloopAI/vibe-kanban · error · ContainerError

Copy project files timed out after 30s

Error message

Copy project files timed out after 30s

What it means

copy_project_files wraps copy_project_files_impl in tokio::task::spawn_blocking with a 30s timeout. If the blocking copy does not finish within 30 seconds, the future is dropped and this error is returned. It indicates the project copy (files/images) is too slow or stalled, not that the copy failed logically.

Source

Thrown at crates/local-deployment/src/container.rs:1599

    /// Skips files that already exist at target with same size.
    async fn copy_project_files(
        &self,
        source_dir: &Path,
        target_dir: &Path,
        copy_files: &str,
    ) -> Result<(), ContainerError> {
        let source_dir = source_dir.to_path_buf();
        let target_dir = target_dir.to_path_buf();
        let copy_files = copy_files.to_string();

        tokio::time::timeout(
            std::time::Duration::from_secs(30),
            tokio::task::spawn_blocking(move || {
                copy::copy_project_files_impl(&source_dir, &target_dir, &copy_files)
            }),
        )
        .await
        .map_err(|_| ContainerError::Other(anyhow!("Copy project files timed out after 30s")))?
        .map_err(|e| ContainerError::Other(anyhow!("Copy files task failed: {e}")))?
    }

    async fn kill_all_running_processes(&self) -> Result<(), ContainerError> {
        tracing::info!("Killing all running processes");
        let running_processes = ExecutionProcess::find_running(&self.db.pool).await?;

        tracing::info!(
            "Found {} running processes to kill",
            running_processes.len()
        );

        for process in running_processes {
            tracing::info!(
                "Killing process: id={}, run_reason={:?}",
                process.id,
                process.run_reason
            );

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Reduce the amount of data copied: exclude build artifacts (node_modules, target, .git) from copy_files.
  2. Copy to a local SSD target instead of a network mount.
  3. Retry the operation once the system load decreases; a stalled copy may succeed on retry.
  4. Increase the timeout constant (Duration::from_secs(30)) if your projects legitimately need longer.

Example fix

// before
std::time::Duration::from_secs(30),
// after
std::time::Duration::from_secs(120), // large projects need more headroom
Defensive patterns

Strategy: retry

Validate before calling

// estimate copy size first
let total: u64 = walkdir::WalkDir::new(&source_dir)
    .into_iter().filter_map(|e| e.ok())
    .filter_map(|e| e.metadata().ok())
    .map(|m| m.len()).sum();
if total > 500_000_000 {
    tracing::warn!(total, "large copy may exceed the 30s timeout");
}

Try / catch

match copy_project_files(...).await {
    Err(ContainerError::Other(e)) if e.to_string().contains("timed out") => {
        tokio::time::sleep(Duration::from_secs(5)).await;
        retry_with_longer_timeout(...).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling copy_files_and_images on a very large project directory, a directory on a slow/network filesystem, or when disk I/O is heavily saturated so copy_project_files_impl exceeds 30s.

Common situations: Copying huge repos with node_modules/target dirs included in copy_files; copying over NFS/network mounts; heavily loaded dev machines; antimalware/Spotlight indexing slowing I/O on macOS/Windows.

Understand the failure class

Related errors


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