clockworklabs/SpacetimeDB · error · anyhow::Error
database is not open
Error message
database is not open
What it means
In the dst (differential system testing) engine target, count_state() dereferences self.db, which is None whenever no database is open. init() always opens one, so in practice this fires after reopen_from_commitlog took the database and the re-open failed — i.e. an earlier Replay interaction failed and left the target without a db, and the next Replay (which calls count_state) or count_state itself hits the None.
Source
Thrown at crates/dst/src/engine.rs:138
}
fn reopen_from_commitlog(&mut self) -> anyhow::Result<()> {
let db = self
.db
.take()
.ok_or_else(|| anyhow::anyhow!("replay without open database"))?;
drop(db);
self.db = Some(Self::open_db(&self.commitlog, self.runtime_handle.clone())?);
Ok(())
}
fn count_state(&self) -> anyhow::Result<CountState> {
let db = self
.db
.as_ref()
.ok_or_else(|| anyhow::anyhow!("database is not open"))?;
let tx = db.begin_tx(Workload::Internal);
let mut row_counts = Vec::with_capacity(self.table_ids.len());
for (table, table_id) in self.table_ids.iter().enumerate() {
let count = match db.iter(&tx, *table_id) {
Ok(iter) => iter.count() as u64,
Err(err) => {
let _ = db.release_tx(tx);
return Err(err.into());
}
};
row_counts.push(TableRowCount { table, count });
}
let _ = db.release_tx(tx);
Ok(CountState { row_counts })
}
View on GitHub (pinned to 6dee26c6ef)
Solutions
- Abort the test run on the first interaction error — target state is no longer valid
- Rebuild the target via EngineTarget::init instead of continuing
- Diagnose the underlying open/replay failure from the first error, not this message
Example fix
// before: keep driving the target after Replay failed
engine.execute(&Interaction::Replay)?;
// after: treat any interaction error as fatal for this run
if let Err(e) = engine.execute(&Interaction::Replay) {
tracing::error!(%e, "target broken; ending run");
return;
} Defensive patterns
Strategy: validation
Validate before calling
fn target_is_usable(target: &EngineTarget, last_ok: bool) -> bool {
last_ok // db is Some after successful init/reopen; a failed Replay leaves it None
} Try / catch
In the driver, match on the execute() Result: on any Err, terminate the run for this target (its db may be gone) rather than issuing further interactions.
Prevention
- Stop DST runs at the first interaction error
- Rebuild the target via EngineTarget::init when a fresh run must continue
- Log the first (root) error, not the cascade of database-is-not-open errors
When it happens
Trigger: Issuing another Replay interaction after a prior Replay already failed inside open_db (commitlog replay error); calling count_state on a target whose init never completed; a driver that ignores an earlier error observation and keeps driving the target.
Common situations: DST/fuzz runs where the workload continues after a failed interaction; test drivers that do not stop on first error.
Related errors
- begin mutable transaction while one is already active
- insert without active mutable transaction
- delete without active mutable transaction
- cannot serialize refs without a typespace
- commit without active mutable transaction
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/c91236fc0e5751b3.
Report an issue: GitHub.