{"record":{"id":"839f94951112bf4e","repo":"loco-rs/loco","slug":"db-cleanup-thread-panicked","errorCode":null,"errorMessage":"db cleanup thread panicked","messagePattern":"db cleanup thread panicked","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/testing/db.rs","lineNumber":156,"sourceCode":"        // it so cleanup fully completes before `cleanup_db` returns. This is\n        // safe to call from `Drop::drop` since nothing here is `.await`ed on\n        // the caller's runtime.\n        std::thread::spawn(move || {\n            let rt = tokio::runtime::Runtime::new().expect(\"create cleanup runtime\");\n\n            rt.block_on(async {\n                let pool = Pool::<Postgres>::connect(&connection_string)\n                    .await\n                    .expect(\"db connection should success\");\n                let query = format!(\"drop database if exists {table_name};\");\n                sqlx::query(AssertSqlSafe(query))\n                    .execute(&pool)\n                    .await\n                    .expect(\"Drop database\");\n            });\n        })\n        .join()\n        .expect(\"db cleanup thread panicked\");\n    }\n}\n\npub struct SqliteTest {\n    connection_string: String,\n    db_folder: PathBuf,\n    _tree: tree_fs::Tree, // Keep the tree alive while the test runs\n}\n\nimpl SqliteTest {\n    /// Prepare new `SQLite` connection string.\n    ///\n    /// # Errors\n    /// Returns an error if could not prepare the connection string\n    pub fn new(conn_str: &str) -> Result<Self> {\n        let db_name = db::extract_db_name(conn_str)?;\n\n        let tree = TreeBuilder::default()","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/loco-rs/loco/blob/23639d1e360dbc618073642b507d6f8664adbaff/src/testing/db.rs#L138-L174","documentation":"After running the async cleanup on a dedicated thread, `cleanup_db` calls `.join()` on the thread handle. If the thread panicked for any reason (any of the inner expects: runtime creation, connect, drop), the join returns Err and this final `.expect(\"db cleanup thread panicked\")` re-panics on the caller, converting a teardown failure into a test-suite failure.","triggerScenarios":"Any panic inside the cleanup thread — 'create cleanup runtime', 'db connection should success', or 'Drop database' — reaching the caller during Drop/test teardown.","commonSituations":"Debugging why a test process aborts at teardown; CI runs where tests pass but the process exits non-zero due to cleanup panics.","solutions":["Fix the root cause reported in the thread panic output (see the earlier 'caused by' message printed before this one)","Make cleanup resilient: catch join errors and log a warning instead of panicking, so flaky DB teardown doesn't fail otherwise-green tests","Run teardown inside the test's own async runtime when possible to avoid the thread+runtime hop entirely","Ensure Postgres availability and clean connection state before teardown (see prior solutions)"],"exampleFix":"// before\n.join()\n.expect(\"db cleanup thread panicked\");\n// after\nif let Err(e) = handle.join() {\n    eprintln!(\"warning: test db cleanup failed: {e:?}\");\n}","handlingStrategy":"fallback","validationCode":"// Pre-check: confirm the cleanup thread's prerequisites (db reachable) before teardown\nlet ok = std::process::Command::new(\"pg_isready\").status().map(|s| s.success()).unwrap_or(false);","typeGuard":null,"tryCatchPattern":"// Convert teardown panics into warnings\nmatch handle.join() {\n    Ok(()) => {},\n    Err(e) => eprintln!(\"warning: test db cleanup thread failed: {e:?}\"),\n}","preventionTips":["Read the inner panic message (printed before this one) and fix the root cause","Make cleanup best-effort with logging rather than panicking","Perform cleanup inside the async test runtime when feasible","Ensure DB availability for the whole test lifecycle, including teardown"],"tags":["rust","testing","threads","panic","database"],"backgroundTag":"thread-interrupted","analyzedSha":"23639d1e360dbc618073642b507d6f8664adbaff","analyzedAt":"2026-09-12T01:47:20.769Z","contentChangedAt":"2026-09-12T01:47:20.769Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}