gitbutlerapp/gitbutler · critical

FATAL: Couldn't open in-memory URL: {path_err}

Error message

FATAL: Couldn't open in-memory URL: {path_err}

What it means

`open_with_migrations_infallible` (but-db/src/cache/mod.rs:70) opens the cache database, falling back to `:memory:` when the on-disk path fails. The `FATAL` panic fires only when the requested path itself is `:memory:` and `rusqlite::Connection::open(":memory:")` fails — i.e. SQLite cannot even create an in-memory database. That indicates resource exhaustion or a broken SQLite build rather than a bad file path; for real paths, open failures degrade to the memory fallback or a contextual `anyhow` error instead.

Source

Thrown at crates/but-db/src/cache/mod.rs:70

/// Like [`run_migrations`], but made so that it cannot fail **and** opens the database either
/// from `path`, removing broken ones on the fly, or from `:memory:` as final fallback,
/// returning `(conn, actual_url)`.
///
/// # Panics
///
/// If in-memory databases can't be opened **and** migrations from zero don't work.
/// Migrations are tested from zero, so that should be impossible.
fn open_with_migrations_infallible<'p, 'm>(
    path: &'p Path,
    migrations: impl IntoIterator<Item = M<'m>> + Clone,
) -> (rusqlite::Connection, &'p Path) {
    let mem_url = ":memory:".as_ref();
    let res = rusqlite::Connection::open(path).map(|c| (c, path));
    let (mut conn, mut path) = res
        .or_else(|path_err| {
            if path == mem_url {
                panic!("FATAL: Couldn't open in-memory URL: {path_err}")
            }
            tracing::warn!(
                "Failed to open cache database at '{path}' with {path_err}, will use memory DB instead",
                path = path.display()
            );
            rusqlite::Connection::open(mem_url)
                .map(|c| (c, mem_url))
                .map_err(|memory_err| {
                    anyhow::Error::from(memory_err).context(path_err).context(format!(
                        "Couldn't open database either from {path} or in memory",
                        path = path.display()
                    ))
                })
        })
        .expect("FATAL: didn't expect to not be able to open an in-memory database at least");

    if let Err(err) = run_migrations(&mut conn, migrations.clone()) {
        assert_ne!(

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Free memory or raise ulimits (fds, address space) in the environment, then retry startup.
  2. Verify a plain probe `rusqlite::Connection::open(":memory:")` works in the same environment to isolate allocator/VFS issues.
  3. If using a bundled/patched SQLite, rebuild with default features so the in-memory VFS is present.
  4. Report the `memory_err`/`path_err` context from the anyhow chain — it names both the original path failure and the memory failure.
Defensive patterns

Strategy: fallback

Validate before calling

// pre-flight: prove SQLite can create an in-memory database before startup
tfn open_memory_probe() -> bool {
    rusqlite::Connection::open(":memory:").is_ok()
}

if !open_memory_probe() {
    // abort early with a clear message instead of the FATAL panic mid-startup
    eprintln!("SQLite cannot open :memory: — check memory limits and the SQLite build");
    std::process::exit(1);
}

Try / catch

let cache = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    open_with_migrations_infallible(Path::new(":memory:"), migrations)
}));
match cache {
    Ok(v) => v,
    Err(_) => {
        // even :memory: failed: free resources, then retry once or exit with diagnostics
        eprintln!("FATAL: in-memory cache unavailable; retrying after cleanup");
        open_with_migrations_infallible(Path::new(":memory:"), migrations)
    }
}

Prevention

When it happens

Trigger: Passing ":memory:" as the cache path while the process is out of memory, at the process/thread limit, or when SQLite's allocator fails; a rusqlite/SQLite build where in-memory VFS support is missing; ulimit or seccomp-sandboxed environments blocking SQLite's temp allocations.

Common situations: Memory-constrained CI containers or sandboxes running with the in-memory cache; exotic cross-compiled targets with a stripped-down SQLite; fork-bomb-style test parallelism exhausting fds/memory before the cache opens.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/189a59b658dc1a1d. Report an issue: GitHub.