BloopAI/vibe-kanban · critical
Failed to copy database file
Error message
Failed to copy database file
What it means
During deployment initialization the server copies the legacy SQLite database (`db.sqlite`) to the new location (`db.v2.sqlite`) so downgrades remain safe. The copy uses `std::fs::copy(...).expect(...)` and panics on any filesystem error. This runs only when the new file does not exist yet and the old one does — a one-time migration on first start after upgrading to the v2 database layout.
Source
Thrown at crates/server/src/startup.rs:151
shutdown: CancellationToken,
) -> Result<DeploymentImpl, DeploymentError> {
// Create asset directory if it doesn't exist
if !asset_dir().exists() {
std::fs::create_dir_all(asset_dir()).map_err(|e| {
DeploymentError::Other(anyhow::anyhow!("Failed to create asset directory: {}", e))
})?;
}
// 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 deployment = DeploymentImpl::new(shutdown).await?;
migrate_legacy_attachment_directories(&deployment).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)?;
deployment
.container()View on GitHub (pinned to 4deb7eca8f)
Solutions
- Free disk space if the copy failed with ENOSPC, then restart
- Fix permissions on the asset directory so the server user can read db.sqlite and write db.v2.sqlite (e.g. `chown -R $USER ~/.vibe-kanban` or equivalent asset dir)
- Check whether db.v2.sqlite was partially created by a failed copy; delete the partial file and restart so the migration retries
- Move the app data out of synced/read-only folders (iCloud Drive, Dropbox) to a normal local directory
- Replace the `.expect()` with a mapped `DeploymentError` so startup fails gracefully with a clear message instead of panicking
Example fix
// before
std::fs::copy(&old_db, &new_db).expect("Failed to copy database file");
// after
std::fs::copy(&old_db, &new_db).map_err(|e| {
DeploymentError::Other(anyhow::anyhow!(
"Failed to copy database {:?} -> {:?}: {}", old_db, new_db, e
))
})?; Defensive patterns
Strategy: validation
Validate before calling
// Preflight the copy before letting the app do it:
fn db_copy_will_succeed(old: &Path, new: &Path) -> bool {
let meta = match std::fs::metadata(old) { Ok(m) => m, Err(_) => return false };
let free = fs4::available_space(old.parent().unwrap()).unwrap_or(0);
free > meta.len() && std::fs::metadata(old.parent().unwrap()).map(|m| !m.permissions().readonly()).unwrap_or(false)
} Try / catch
// Catch the migration panic when embedding initialize_deployment:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
// run initialization on a thread
}));
if result.is_err() {
eprintln!("DB migration failed — check disk space and permissions on the asset dir, delete partial db.v2.sqlite, and retry");
} Prevention
- Ensure adequate free disk space before upgrading the app
- Keep the asset/data directory out of synced or read-only folders (iCloud, Dropbox)
- Run the app consistently as the same user so file ownership stays correct
- Delete partially written db.v2.sqlite files after a failed migration
- Patch the code to return DeploymentError instead of expect() on fs::copy
When it happens
Trigger: `initialize_deployment` finds `asset_dir()/db.sqlite` present and `asset_dir()/db.v2.sqlite` absent, then `std::fs::copy` fails — typically due to insufficient disk space, permission denied on the asset directory, the old file being unreadable/locked, or an I/O error during the copy.
Common situations: Disk-full when the database is large; asset directory owned by a different user after running the app with sudo once; read-only or sync-conflicted directory (e.g. files inside Dropbox/iCloud folders); antivirus or backup software holding the sqlite file open on Windows.
Related errors
- Copy files task failed: {e}
- Migration failed: {}
- Migration DB pool error: {}
- Failed to create asset directory
- OS didn't give us a home directory
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/f7646d33151edacb.
Report an issue: GitHub.