BloopAI/vibe-kanban · critical
Failed to copy database file
Error message
Failed to copy database file
What it means
During startup, main() migrates the SQLite database from an old location to a new one by calling std::fs::copy. The code uses .expect("Failed to copy database file"), so any I/O failure while copying aborts the process with this panic message. The authors treat a failed migration as unrecoverable because the server cannot run without its database at the expected path.
Source
Thrown at crates/server/src/main.rs:66
.with(tracing_subscriber::fmt::layer().with_filter(env_filter))
.with(sentry_layer())
.init();
// Create asset directory if it doesn't exist
if !asset_dir().exists() {
std::fs::create_dir_all(asset_dir())?;
}
// Copy old database to new location for safe downgrades
let old_db = asset_dir().join("db.sqlite");
let new_db = asset_dir().join("db.v2.sqlite");
if !new_db.exists() && old_db.exists() {
tracing::info!(
"Copying database to new location: {:?} -> {:?}",
old_db,
new_db
);
std::fs::copy(&old_db, &new_db).expect("Failed to copy database file");
tracing::info!("Database copy complete");
}
let shutdown_token = CancellationToken::new();
let deployment = DeploymentImpl::new(shutdown_token.clone()).await?;
deployment.update_sentry_scope().await?;
deployment
.container()
.cleanup_orphan_executions()
.await
.map_err(DeploymentError::from)?;
deployment
.container()
.backfill_before_head_commits()
.await
.map_err(DeploymentError::from)?;
deploymentView on GitHub (pinned to 4deb7eca8f)
Solutions
- Ensure the destination directory for new_db exists and is writable; create it with fs::create_dir_all before startup.
- Verify no other server instance is running and holding the old database file (lsof/handle).
- Fix filesystem permissions on old_db and the destination directory, or run with sufficient privileges.
- Check free disk space is sufficient for a copy of the database.
- As manual recovery, copy the database file to the new location yourself and restart.
Example fix
// before
std::fs::copy(&old_db, &new_db).expect("Failed to copy database file");
// after
if let Some(parent) = new_db.parent() {
std::fs::create_dir_all(parent).expect("Failed to create db directory");
}
std::fs::copy(&old_db, &new_db)
.unwrap_or_else(|e| panic!("Failed to copy database file {old_db:?} -> {new_db:?}: {e}")); Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
fn can_migrate_db(old: &Path, new: &Path) -> Result<(), String> {
if !old.exists() { return Ok(()); }
if old.is_dir() { return Err("old db path is a directory".into()); }
if let Some(parent) = new.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| format!("dest dir not creatable: {e}"))?;
}
let meta = std::fs::metadata(old)
.map_err(|e| format!("old db unreadable: {e}"))?;
if meta.permissions().readonly() { return Err("old db is read-only".into()); }
Ok(())
} Try / catch
match std::fs::copy(&old_db, &new_db) {
Ok(n) => tracing::info!("copied {n} bytes"),
Err(e) => {
tracing::error!("db migration copy failed: {e}");
return Err(e.into());
}
} Prevention
- Always create the destination directory with create_dir_all before copying.
- Check free disk space before large file copies.
- Use a pidfile/lock so only one server instance runs at a time.
- Prefer Result propagation over .expect in startup code so failures are diagnosable.
When it happens
Trigger: std::fs::copy(&old_db, &new_db) fails: old_db exists but is not readable (permissions, locked by another process), new_db's parent directory does not exist or is not writable, disk full, or old_db is a directory. A race where another instance creates new_db after the exists() check can also cause a failure.
Common situations: Upgrading from an older app version where the DB lived at a different path; two server instances running simultaneously; read-only volume or container filesystem permissions; old DB file held open/locked by another process (common on Windows); config dir changed so the new path's directory was never created.
Related errors
- Migration failed: {}
- Copy project files timed out after 30s
- Migration DB pool error: {}
- Failed to create asset directory
- Failed to load orchestrator MCP context from /api/containers
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/4b4b3061030202a7.
Report an issue: GitHub.