tursodatabase/turso · error · Error

Database is closed

Error message

Database is closed

What it means

checkOpen() is the private precondition for query/exec flows on the React Native Database and throws 'Database is closed' when _closed is true — i.e., after close() was called. Everything on the instance becomes unusable from that point, including prepared-statement operations that route through the connection. The database cannot be reopened; construct a new Database instead.

Source

Thrown at bindings/react-native/src/Database.ts:480

    return !this._connection.getAutocommit();
  }

  /**
   * Get last insert rowid
   */
  get lastInsertRowid(): number {
    if (!this._connection) {
      return 0;
    }
    return this._connection.lastInsertRowid();
  }

  /**
   * Check if open and throw if not
   */
  private checkOpen(): void {
    if (this._closed) {
      throw new Error('Database is closed');
    }
    if (!this._connected || !this._connection) {
      throw new Error('Database not connected. Call connect() first.');
    }
  }
}

View on GitHub (pinned to bad083fafb)

Solutions

  1. Create a fresh Database instance (and connect()) after closing — close() is terminal for the handle
  2. Await all in-flight operations before calling close(), and null out references so stale closures cannot use the handle
  3. Track lifecycle state in your own wrapper (open/connect/close) and check it before issuing queries

Example fix

// before
db.close();
await db.query('SELECT 1'); // throws: Database is closed

// after
await inFlightOps;
db.close();
db = new Database(opts); // if needed again
await db.connect();
await db.query('SELECT 1');
Defensive patterns

Strategy: try-catch

Validate before calling

// Track lifecycle in your own wrapper — the binding's _closed is private
class DbManager {
  private handle: Database | null = null;
  get db(): Database {
    if (!this.handle) throw new Error('Database closed — call open() first');
    return this.handle;
  }
  async open(opts: DatabaseOpts) { this.handle = new Database(opts); await this.handle.connect(); }
  async close() { await this.handle?.close(); this.handle = null; }
}

Try / catch

try { await db.query(sql); } catch (e) { if (e instanceof Error && e.message === 'Database is closed') { db = await openDatabase(opts); return db.query(sql); } throw e; }

Prevention

When it happens

Trigger: Calling `db.query(...)`, `db.exec(...)`, or preparing a statement after `db.close()`; React component unmount closing the DB while an in-flight sibling operation or a later user action still uses the handle; shutdown hooks closing the database before a final flush completes.

Common situations: App shutdown or logout flows that close eagerly; StrictMode double-mounts in React 18 closing on cleanup while another effect still queries; long-lived singletons captured in closures that outlive the close; error-recovery code that closes on failure but retry paths reuse the handle.

Related errors


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