tursodatabase/turso · error · TypeError

The database connection is not open

Error message

The database connection is not open

What it means

Connection.prepare() checks the private isOpen flag and throws a plain TypeError 'The database connection is not open' when the connection has been closed. close() sets isOpen = false before closing the session, and there is no public isOpen getter on Connection, so callers must track lifecycle themselves or use reconnect() to reopen the same object.

Source

Thrown at serverless/javascript/src/connection.ts:153

  /**
   * Prepare a SQL statement for execution.
   * 
   * Prepared statements created from a Connection use the same underlying session so transaction boundaries are preserved.
   * This method fetches column metadata using the describe functionality.
   * 
   * @param sql - The SQL statement to prepare
   * @returns A Promise that resolves to a Statement object with column metadata
   * 
   * @example
   * ```typescript
   * const stmt = await client.prepare("SELECT * FROM users WHERE id = ?");
   * const columns = stmt.columns();
   * const user = await stmt.get([123]);
   * ```
   */
  async prepare(sql: string): Promise<Statement> {
    if (!this.isOpen) {
      throw new TypeError("The database connection is not open");
    }

    // Describe on the existing session so it sees uncommitted DDL
    // (e.g. CREATE TABLE in the same transaction).
    await this.execLock.acquire();
    let description;
    try {
      description = await this.session.describe(sql);
    } finally {
      this.execLock.release();
    }

    const stmt = Statement.fromSession(this.session, sql, description.cols, this.execLock);
    if (this.defaultSafeIntegerMode) {
      stmt.safeIntegers(true);
    }
    return stmt;
  }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Call await db.reconnect() to reopen the same Connection object, then retry prepare()
  2. Create a new Connection via connect(config) instead of reusing the closed one
  3. Remove Connection objects from your pool/registry when you close them so they are never handed out again
  4. Ensure close() only runs after all users of the connection are finished

Example fix

// before
await db.close();
const stmt = await db.prepare('SELECT * FROM users WHERE id = ?');
// TypeError: The database connection is not open

// after
if (connectionWasClosed) {
  await db.reconnect();
}
const stmt = await db.prepare('SELECT * FROM users WHERE id = ?');
Defensive patterns

Strategy: try-catch

Validate before calling

// Connection exposes no public isOpen getter — track lifecycle at the call site.
let db = connect(config);
let dbOpen = true;

async function closeDb() {
  await db.close();
  dbOpen = false;
}

async function prepareSafe(sql: string) {
  if (!dbOpen) {
    await db.reconnect();
    dbOpen = true;
  }
  return db.prepare(sql);
}

Try / catch

try {
  return await db.prepare(sql);
} catch (e) {
  if (e instanceof TypeError && e.message.includes('not open')) {
    await db.reconnect();
    return db.prepare(sql);
  }
  throw e;
}

Prevention

When it happens

Trigger: await db.close(); await db.prepare(sql). Reusing a pooled Connection object after a maintenance path closed it. An error path closing the connection, then normal flow continuing to prepare statements.

Common situations: Connection pools that close idle connections but hand them out again; retry logic that closes on a network error and later prepares; long-lived server code where a shutdown hook closed the connection before late requests arrive.

Related errors


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