loco-rs/loco · error

create cleanup runtime

Error message

create cleanup runtime

What it means

`cleanup_db` runs DB teardown from a synchronous context (including `Drop`) by spawning a dedicated OS thread that builds a fresh tokio `Runtime`. `Runtime::new()` failing — e.g. the process is out of resources or a runtime limitation is hit — panics with 'create cleanup runtime', aborting cleanup and typically surfacing during test teardown.

Solutions

  1. Reduce test parallelism (`--test-threads`) or the number of concurrently live test DBs so fewer cleanup threads/runtimes exist
  2. Raise container/thread limits (`ulimit -u`, more memory in CI)
  3. Prefer explicit async cleanup (call teardown inside the test's runtime) over relying on sync Drop-based cleanup
  4. Check tokio feature flags are the standard ones; do not globally disable rt features in dependency overrides
Defensive patterns

Strategy: fallback

Validate before calling

// Sanity-check headroom for spawning the cleanup runtime
let max_threads = std::fs::read_to_string("/proc/sys/kernel/threads-max")
    .ok().and_then(|s| s.trim().parse::<i64>().ok());
println!("threads-max = {:?}", max_threads); // low values risk Runtime::new failure

Try / catch

// Prefer explicit async cleanup inside the test runtime over Drop-based sync cleanup
// async test body:
//   test_support.cleanup().await; // instead of relying on Drop

Prevention

When it happens

Trigger: Calling cleanup when the process cannot allocate a new tokio runtime (thread/resource exhaustion, `set_current_thread` runtime builder misconfiguration via env, extremely constrained containers).

Common situations: Huge parallel test suites hitting thread limits; container memory limits killing runtime bootstrap; exotic runtimes where tokio cannot spawn its blocking pool.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of loco-rs/loco@23639d1e36 (2026-09-12). Data as JSON: /api/errors/5a6d2e9c81bf609f. Report an issue: GitHub.

Appendix: source

Thrown at src/testing/db.rs:142

            sqlx::query(AssertSqlSafe(query))
                .execute(&pool)
                .await
                .expect("create DB schema");
        })
    }

    fn cleanup_db(&self) {
        let connection_string = self.root_connection_string.clone();
        let table_name = self.schema_name.clone();

        // Run the drop on a dedicated OS thread with its own runtime (not
        // `tokio::task::spawn_blocking`, which schedules onto the ambient
        // runtime and cannot be awaited from a sync/Drop context) and `.join()`
        // 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 {

View on GitHub (pinned to 23639d1e36)