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

  1. Free disk space if the copy failed with ENOSPC, then restart
  2. 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)
  3. Check whether db.v2.sqlite was partially created by a failed copy; delete the partial file and restart so the migration retries
  4. Move the app data out of synced/read-only folders (iCloud Drive, Dropbox) to a normal local directory
  5. 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

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


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