{"record":{"id":"4b4b3061030202a7","repo":"BloopAI/vibe-kanban","slug":"failed-to-copy-database-file","errorCode":null,"errorMessage":"Failed to copy database file","messagePattern":"Failed to copy database file","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/server/src/main.rs","lineNumber":66,"sourceCode":"        .with(tracing_subscriber::fmt::layer().with_filter(env_filter))\n        .with(sentry_layer())\n        .init();\n\n    // Create asset directory if it doesn't exist\n    if !asset_dir().exists() {\n        std::fs::create_dir_all(asset_dir())?;\n    }\n\n    // Copy old database to new location for safe downgrades\n    let old_db = asset_dir().join(\"db.sqlite\");\n    let new_db = asset_dir().join(\"db.v2.sqlite\");\n    if !new_db.exists() && old_db.exists() {\n        tracing::info!(\n            \"Copying database to new location: {:?} -> {:?}\",\n            old_db,\n            new_db\n        );\n        std::fs::copy(&old_db, &new_db).expect(\"Failed to copy database file\");\n        tracing::info!(\"Database copy complete\");\n    }\n\n    let shutdown_token = CancellationToken::new();\n\n    let deployment = DeploymentImpl::new(shutdown_token.clone()).await?;\n    deployment.update_sentry_scope().await?;\n    deployment\n        .container()\n        .cleanup_orphan_executions()\n        .await\n        .map_err(DeploymentError::from)?;\n    deployment\n        .container()\n        .backfill_before_head_commits()\n        .await\n        .map_err(DeploymentError::from)?;\n    deployment","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/server/src/main.rs#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nstd::fs::copy(&old_db, &new_db).expect(\"Failed to copy database file\");\n// after\nif let Some(parent) = new_db.parent() {\n    std::fs::create_dir_all(parent).expect(\"Failed to create db directory\");\n}\nstd::fs::copy(&old_db, &new_db)\n    .unwrap_or_else(|e| panic!(\"Failed to copy database file {old_db:?} -> {new_db:?}: {e}\"));","handlingStrategy":"validation","validationCode":"use std::path::Path;\nfn can_migrate_db(old: &Path, new: &Path) -> Result<(), String> {\n    if !old.exists() { return Ok(()); }\n    if old.is_dir() { return Err(\"old db path is a directory\".into()); }\n    if let Some(parent) = new.parent() {\n        std::fs::create_dir_all(parent)\n            .map_err(|e| format!(\"dest dir not creatable: {e}\"))?;\n    }\n    let meta = std::fs::metadata(old)\n        .map_err(|e| format!(\"old db unreadable: {e}\"))?;\n    if meta.permissions().readonly() { return Err(\"old db is read-only\".into()); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match std::fs::copy(&old_db, &new_db) {\n    Ok(n) => tracing::info!(\"copied {n} bytes\"),\n    Err(e) => {\n        tracing::error!(\"db migration copy failed: {e}\");\n        return Err(e.into());\n    }\n}","preventionTips":["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."],"tags":["filesystem","startup","database","io"],"backgroundTag":"database-file-copy-failed","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}