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
- Free memory or raise ulimits (fds, address space) in the environment, then retry startup.
- Verify a plain probe `rusqlite::Connection::open(":memory:")` works in the same environment to isolate allocator/VFS issues.
- If using a bundled/patched SQLite, rebuild with default features so the in-memory VFS is present.
- 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
- Run memory-pressured workloads with ulimits sized for SQLite's page cache allocations.
- Validate the SQLite/rusqlite build in new environments with a :memory: probe in smoke tests.
- Monitor open connection counts so allocator failures surface as actionable alerts, not startup panics.
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
- just set the value
- Could not get app cache dir
- Failed to create runtime: {e}
- failed to create tokio runtime
- Failed to communicate with LM Studio server: ${error instanc
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/189a59b658dc1a1d.
Report an issue: GitHub.