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)?;
    deployment

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Ensure the destination directory for new_db exists and is writable; create it with fs::create_dir_all before startup.
  2. Verify no other server instance is running and holding the old database file (lsof/handle).
  3. Fix filesystem permissions on old_db and the destination directory, or run with sufficient privileges.
  4. Check free disk space is sufficient for a copy of the database.
  5. 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

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


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