{"record":{"id":"b5bc5e2872edfc6f","repo":"remix-run/remix","slug":"sqlite-database-is-closed","errorCode":null,"errorMessage":"SQLite database is closed","messagePattern":"SQLite database is closed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/data-table-sqlite/src/lib/driver.ts","lineNumber":470,"sourceCode":"    return this.#config\n  }\n\n  #assertNoOpenTransactions(method: string): void {\n    if (this.#transactions.size > 0) {\n      throw new Error('SQLite database cannot ' + method + ' while transactions are open')\n    }\n  }\n\n  #assertTransaction(token: TransactionToken): void {\n    this.#assertDatabaseOpen()\n    if (!this.#transactions.has(token.id)) {\n      throw new Error('Unknown transaction token: ' + token.id)\n    }\n  }\n\n  #assertDatabaseOpen(): void {\n    if (!this.#databaseOpen) {\n      throw new Error('SQLite database is closed')\n    }\n  }\n}\n\nconst REMOVE_RETRIES = 10\nconst REMOVE_RETRY_DELAY_MS = 100\n\nasync function removeDatabaseFile(filename: string): Promise<void> {\n  // Windows keeps a just-closed database file locked for a short window (deferred handle\n  // release, antivirus scans), so removal is retried with a linear backoff\n  for (let attempt = 0; ; attempt++) {\n    try {\n      await rm(filename, { force: true })\n      return\n    } catch (error) {\n      if (attempt >= REMOVE_RETRIES || !isRetryableRemoveError(error)) {\n        throw error\n      }","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table-sqlite/src/lib/driver.ts#L452-L488","documentation":"Thrown by the SQLite data-table driver when an operation is attempted after the underlying SQLite database has been closed. The driver tracks an internal #databaseOpen flag; #assertDatabaseOpen() runs before execute, executeScript, hasTable, hasColumn, and transaction methods and throws immediately if the flag is false. It protects against use-after-close on a driver instance.","triggerScenarios":"Calling db.execute(...), db.executeScript(...), db.hasTable(...), db.hasColumn(...), beginTransaction(), or any statement inside a transaction after close() (or a failed open/remove cycle) has already run on the same SQLite driver instance.","commonSituations":"App shutdown handlers that close the DB then a pending request or migration still tries to query; reusing a module-level driver singleton after tests call afterAll/close; hot-reload creating a new database file lifecycle while old handles retry writes.","solutions":["Ensure close() is only called after all in-flight queries, migrations, and transactions have settled (track outstanding promises before closing).","If you need the database again after closing, create and open a fresh driver/database instance instead of reusing the closed one.","In tests, use beforeEach/afterEach pairs so each test opens and closes its own driver and no shared instance outlives close."],"exampleFix":"// before\nawait db.close()\nawait db.hasTable('users') // throws\n\n// after\nawait db.close()\ndb = new SqliteDriver(...) // reopen a fresh instance\nawait db.hasTable('users')","handlingStrategy":"try-catch","validationCode":"// Track closes in your own wrapper since the flag is private\nlet closed = false\nexport async function withDb<T>(fn: () => Promise<T>): Promise<T> {\n  if (closed) throw new Error('db already closed')\n  return await fn()\n}","typeGuard":null,"tryCatchPattern":"try {\n  await db.hasTable('users')\n} catch (error) {\n  if (error instanceof Error && error.message === 'SQLite database is closed') {\n    // reopen a new driver instance or bail gracefully\n  }\n}","preventionTips":["Track close() in app lifecycle code and gate new queries on it.","Await all in-flight queries before closing in shutdown handlers.","In tests, open/close the driver per test rather than sharing one instance."],"tags":["sqlite","lifecycle","use-after-close","data-table"],"backgroundTag":"database-connection-closed","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}