tursodatabase/turso · error · TypeError

The database connection is not open

Error message

The database connection is not open

What it means

Thrown by Database.prepare() in the better-sqlite3 compatibility layer when you try to prepare a statement on a connection whose native handle is no longer open. The `open` property is a live getter over the native database, so once close() has been called (or the connection was never successfully opened), any subsequent prepare() is rejected immediately with this TypeError instead of crashing the native layer.

Source

Thrown at bindings/javascript/packages/common/compat.ts:159

    this.db.connectSync();

    Object.defineProperties(this, {
      name: { get: () => this.db.path },
      readonly: { get: () => this.db.readonly },
      open: { get: () => this.db.open },
      memory: { get: () => this.db.memory },
      inTransaction: { get: () => this.db.inTransaction() },
    });
  }

  /**
   * Prepares a SQL statement for execution.
   *
   * @param {string} sql - The SQL statement string to prepare.
   */
  prepare(sql) {
    if (!this.open) {
      throw new TypeError("The database connection is not open");
    }
    if (!sql) {
      throw new RangeError("The supplied SQL string contains no statements");
    }

    try {
      return new Statement(this.db.prepare(sql), this.db);
    } catch (err) {
      throw convertError(err);
    }
  }

  /**
   * Returns a function that executes the given function in a transaction.
   *
   * @param {function} fn - The function to wrap in a transaction.
   */
  transaction(fn) {

View on GitHub (pinned to bad083fafb)

Solutions

  1. Check `db.open` before preparing: if (db.open) stmt = db.prepare(sql)
  2. Reorder shutdown: drain in-flight queries first, then close the connection (or use an async-dispose queue)
  3. If the connection was closed unexpectedly, reopen it (recreate the Database) and retry the operation
  4. Audit for double-close or premature close paths (SIGINT handlers, test teardown, connection-pool eviction)

Example fix

// before
const row = db.prepare('SELECT 1').get(); // db was closed earlier

// after
if (!db.open) db = new Database(...); // or skip/reopen
const row = db.prepare('SELECT 1').get();
Defensive patterns

Strategy: validation

Validate before calling

if (!db.open) {
  throw new Error('database is closed; reopen before preparing statements');
}
const stmt = db.prepare(sql);

Type guard

function isOpen(db: { open: boolean }): boolean {
  return db.open === true;
}

Try / catch

try {
  stmt = db.prepare(sql);
} catch (err) {
  if (err instanceof TypeError && err.message === 'The database connection is not open') {
    db = new Database(path); // or mark connection stale and reschedule
    stmt = db.prepare(sql);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling db.prepare(sql) after db.close(); calling prepare() during shutdown of a long-lived process (e.g. after a SIGTERM handler closed the DB but a queued query still runs); sharing a Database instance across modules where one path closes it; calling prepare() after an unrecoverable native error closed the connection.

Common situations: Unit tests that close the DB in afterEach but a stray test still queries it; hot-reload in dev servers that closes the old connection while in-flight requests still hold a reference to the old Database object; graceful-shutdown code that closes the DB before draining the request queue.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/5148e5cc335f4d8c. Report an issue: GitHub.