tursodatabase/turso · error · Error

Database is closed

Error message

Database is closed

What it means

checkOpen() throws this when _closed is true — i.e. after db.close() ran. close() closes the native connection and database handles and sets _closed last, so every later prepare(), exec(), transaction, or getter that routes through checkOpen() fails fast instead of touching freed native resources.

Solutions

  1. Check `db.open` before issuing statements after any point where close() may have run
  2. Reopen by constructing a new Database instance and awaiting connect() rather than reusing the closed one
  3. Cancel/await in-flight work before close() so nothing enqueues queries afterward
  4. On logout flows, null out module-level db references after close() so stale callers fail visibly in dev

Example fix

// before
let db = new Database({ path: getDbPath('app.db') });
await db.connect();
// later, after logout:
db.close();
const rows = await db.all('SELECT 1'); // throws 'Database is closed'

// after
if (!db.open) {
  db = new Database({ path: getDbPath('app.db') });
  await db.connect();
}
const rows = await db.all('SELECT 1');
Defensive patterns

Strategy: validation

Validate before calling

if (!db.open) {
  db = new Database({ path: getDbPath('app.db') });
  await db.connect();
}
await db.prepare('SELECT 1');

Type guard

function isOpen(db: Database): boolean {
  return db.open; // false once close() ran
}

Try / catch

try {
  await db.prepare(sql);
} catch (e) {
  if (e instanceof Error && e.message === 'Database is closed') {
    db = await reopenDatabase(); // fresh instance + connect
    return db.prepare(sql);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling db.prepare(sql), db.exec(sql), db.transaction(...), etc. after db.close() has already executed; a late async operation (queued query, retry timer, background sync loop) firing after the shutdown path closed the database.

Common situations: Closing the db in a component unmount / app exit handler while other code still holds a reference and issues queries; test files closing a shared database in afterEach but reusing it in a later test; caching a Database in a module-level variable that survives logout/login cycles where logout closes it.

Related errors


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

Appendix: 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 244cde92a7)