{"record":{"id":"5f02ff63c7e0f57e","repo":"tursodatabase/turso","slug":"database-is-closed","errorCode":null,"errorMessage":"Database is closed","messagePattern":"Database is closed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"bindings/react-native/src/Database.ts","lineNumber":480,"sourceCode":"    return !this._connection.getAutocommit();\n  }\n\n  /**\n   * Get last insert rowid\n   */\n  get lastInsertRowid(): number {\n    if (!this._connection) {\n      return 0;\n    }\n    return this._connection.lastInsertRowid();\n  }\n\n  /**\n   * Check if open and throw if not\n   */\n  private checkOpen(): void {\n    if (this._closed) {\n      throw new Error('Database is closed');\n    }\n    if (!this._connected || !this._connection) {\n      throw new Error('Database not connected. Call connect() first.');\n    }\n  }\n}\n","sourceCodeStart":462,"sourceCodeEnd":487,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/react-native/src/Database.ts#L462-L487","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check `db.open` before issuing statements after any point where close() may have run","Reopen by constructing a new Database instance and awaiting connect() rather than reusing the closed one","Cancel/await in-flight work before close() so nothing enqueues queries afterward","On logout flows, null out module-level db references after close() so stale callers fail visibly in dev"],"exampleFix":"// before\nlet db = new Database({ path: getDbPath('app.db') });\nawait db.connect();\n// later, after logout:\ndb.close();\nconst rows = await db.all('SELECT 1'); // throws 'Database is closed'\n\n// after\nif (!db.open) {\n  db = new Database({ path: getDbPath('app.db') });\n  await db.connect();\n}\nconst rows = await db.all('SELECT 1');","handlingStrategy":"validation","validationCode":"if (!db.open) {\n  db = new Database({ path: getDbPath('app.db') });\n  await db.connect();\n}\nawait db.prepare('SELECT 1');","typeGuard":"function isOpen(db: Database): boolean {\n  return db.open; // false once close() ran\n}","tryCatchPattern":"try {\n  await db.prepare(sql);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Database is closed') {\n    db = await reopenDatabase(); // fresh instance + connect\n    return db.prepare(sql);\n  }\n  throw e;\n}","preventionTips":["Null out Database references after close() so stale callers fail visibly in development","Await pending work before closing; close exactly once at real teardown","Check db.open before queries in code that may run after logout/background expiry"],"tags":["react-native","lifecycle","closed-resource","database"],"backgroundTag":"use-after-close","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}