remix-run/remix · error · Error

MySQL database cannot + method + while transactions are op

Error message

MySQL database cannot  + method +  while transactions are open

What it means

The driver refuses destructive lifecycle operations (wipe, close) while transactions are still open, tracked in its internal #transactions map. #assertNoOpenTransactions throws with the offending method name to prevent dropping or wiping a database with in-flight transactions that would be left in limbo.

Source

Thrown at packages/data-table-mysql/src/lib/driver.ts:485

    await this.#closePool().catch(() => undefined)

    if (this.#config) {
      this.#client = createMysqlPool(this.#config)
      this.#poolClosed = false
    }
  }

  #configOrThrow(method: string): string | MysqlPoolOptions {
    if (!this.#config) {
      throw new Error('MySQL database ' + method + '() requires config-based construction')
    }

    return this.#config
  }

  #assertNoOpenTransactions(method: string): void {
    if (this.#transactions.size > 0) {
      throw new Error('MySQL database cannot ' + method + ' while transactions are open')
    }
  }

  #resolveClient(token: TransactionToken | undefined): MysqlQueryable {
    if (!token) {
      return this.#client
    }

    return this.#transactionConnection(token)
  }

  #transactionConnection(token: TransactionToken): MysqlTransactionConnection {
    let transaction = this.#transactions.get(token.id)

    if (!transaction) {
      throw new Error('Unknown transaction token: ' + token.id)
    }

View on GitHub (pinned to 9696913134)

Solutions

  1. Commit or roll back every outstanding transaction before calling wipe()/close()
  2. Use try/finally around transaction bodies so errors always roll back
  3. In test teardown, drain or assert zero open transactions before wiping

Example fix

// before
const token = await driver.beginTransaction()
await doWork(driver, token)
// error path skips commit
await driver.wipe() // throws
// after
const token = await driver.beginTransaction()
try {
  await doWork(driver, token)
  await driver.commitTransaction(token)
} catch (e) {
  await driver.rollbackTransaction(token)
  throw e
}
await driver.wipe()
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure finalization in a helper
async function withTransaction<T>(driver, fn: (t: TransactionToken) => Promise<T>) {
  const token = await driver.beginTransaction()
  try { const r = await fn(token); await driver.commitTransaction(token); return r }
  catch (e) { await driver.rollbackTransaction(token); throw e }
}

Try / catch

try { await driver.wipe() } catch (e) { if (e instanceof Error && e.message.includes('while transactions are open')) { /* finalize leaked transactions, then retry */ } throw e }

Prevention

When it happens

Trigger: Calling driver.wipe() or driver.close() after beginTransaction without a matching commit/rollback; test teardown wiping the database while a leaked transaction token was never finalized.

Common situations: Test suites that begin transactions and error out before finalization, then wipe between tests; missing rollback in a catch block; async operations still in flight when close() is called during shutdown.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/b8d4408c0ecf262d. Report an issue: GitHub.