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, ©_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
- Reduce the amount of data copied: exclude build artifacts (node_modules, target, .git) from copy_files.
- Copy to a local SSD target instead of a network mount.
- Retry the operation once the system load decreases; a stalled copy may succeed on retry.
- 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
- Exclude node_modules/target/.git from copy_files.
- Keep source and target on local SSDs, not network mounts.
- Scale the timeout with project size.
- Alert on repeated copy timeouts.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Operation forcibly terminated due to hard timeout
- Failed to copy database file
- Failed to create asset directory
- Approval has timed out
- Timeout: process took more than 30 seconds to start
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/7d062b9143d5a37d.
Report an issue: GitHub.