loco-rs/loco · error
db cleanup thread panicked
Error message
db cleanup thread panicked
What it means
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.
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)
Example fix
// before
.join()
.expect("db cleanup thread panicked");
// after
if let Err(e) = handle.join() {
eprintln!("warning: test db cleanup failed: {e:?}");
} Defensive patterns
Strategy: fallback
Validate before calling
// Pre-check: confirm the cleanup thread's prerequisites (db reachable) before teardown
let ok = std::process::Command::new("pg_isready").status().map(|s| s.success()).unwrap_or(false); Try / catch
// Convert teardown panics into warnings
match handle.join() {
Ok(()) => {},
Err(e) => eprintln!("warning: test db cleanup thread failed: {e:?}"),
} Prevention
- 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
When it happens
Trigger: Any panic inside the cleanup thread — 'create cleanup runtime', 'db connection should success', or 'Drop database' — reaching the caller during Drop/test teardown.
Common situations: Debugging why a test process aborts at teardown; CI runs where tests pass but the process exits non-zero due to cleanup panics.
Related errors
- db connection should success
- create DB schema
- create cleanup runtime
- Drop database
- implement `up()` for the
AI-assisted analysis of loco-rs/loco@23639d1e36 (2026-09-12).
Data as JSON: /api/errors/839f94951112bf4e.
Report an issue: GitHub.
Appendix: source
Thrown at src/testing/db.rs:156
// it so cleanup fully completes before `cleanup_db` returns. This is
// safe to call from `Drop::drop` since nothing here is `.await`ed on
// the caller's runtime.
std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().expect("create cleanup runtime");
rt.block_on(async {
let pool = Pool::<Postgres>::connect(&connection_string)
.await
.expect("db connection should success");
let query = format!("drop database if exists {table_name};");
sqlx::query(AssertSqlSafe(query))
.execute(&pool)
.await
.expect("Drop database");
});
})
.join()
.expect("db cleanup thread panicked");
}
}
pub struct SqliteTest {
connection_string: String,
db_folder: PathBuf,
_tree: tree_fs::Tree, // Keep the tree alive while the test runs
}
impl SqliteTest {
/// Prepare new `SQLite` connection string.
///
/// # Errors
/// Returns an error if could not prepare the connection string
pub fn new(conn_str: &str) -> Result<Self> {
let db_name = db::extract_db_name(conn_str)?;
let tree = TreeBuilder::default()View on GitHub (pinned to 23639d1e36)