payloadcms/payload · critical · Error

Error: cannot connect to SQLite: ${message}

Error message

Error: cannot connect to SQLite: ${message}

What it means

The D1 SQLite adapter's connect wraps drizzle(binding) setup, optional readReplica binding selection, and dropDatabase in a try/catch. Any thrown error is logged via payload.logger, rejects initialization, and is re-thrown with the original message inlined. This is a pass-through wrapper, not the root cause.

Source

Thrown at packages/db-d1-sqlite/src/connect.ts:54

    })

    this.client = this.drizzle.$client as any

    if (!hotReload) {
      if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
        this.payload.logger.info(`---- DROPPING TABLES ----`)
        await this.dropDatabase({ adapter: this })
        this.payload.logger.info('---- DROPPED TABLES ----')
      }
    }
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err)
    this.payload.logger.error({ err, msg: `Error: cannot connect to SQLite: ${message}` })
    if (typeof this.rejectInitializing === 'function') {
      this.rejectInitializing()
    }
    console.error(err)
    throw new Error(`Error: cannot connect to SQLite: ${message}`)
  }

  // Only push schema if not in production
  if (
    process.env.NODE_ENV !== 'production' &&
    process.env.PAYLOAD_MIGRATING !== 'true' &&
    this.push !== false
  ) {
    await pushDevSchema(this as unknown as DrizzleAdapter)
  }

  if (typeof this.resolveInitializing === 'function') {
    this.resolveInitializing()
  }

  if (process.env.NODE_ENV === 'production' && this.prodMigrations) {
    await this.migrate({ migrations: this.prodMigrations as Migration[] })
  }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Read the inlined `message` — it identifies the underlying failure (e.g. 'Cannot read properties of undefined (reading ...)' means this.binding is missing).
  2. Ensure a D1 binding is provided to the adapter config and matches the binding name in wrangler.toml [[d1_databases]].
  3. When testing locally, run through wrangler/dev so the D1 binding is injected, rather than plain node.
  4. If readReplicas is set to 'first-primary', confirm your D1 binding supports withSession (remove the option if not).

Example fix

// before — adapter created with no binding
new SQLiteD1Adapter({ payload, binding: undefined })

// after — provide the bound D1 database
new SQLiteD1Adapter({
  payload,
  binding: env.MY_D1_DB, // matches [[d1_databases]] binding = "MY_D1_DB"
})
Defensive patterns

Strategy: validation

Validate before calling

function assertD1Binding(adapter: { binding?: unknown }) {
  if (!adapter.binding || typeof (adapter.binding as any).prepare !== 'function') {
    throw new Error('D1 binding missing or invalid — provide a bound D1 database (env.BINDING_NAME)')
  }
}

Type guard

function isD1Database(x: unknown): x is { prepare: (sql: string) => unknown } {
  return typeof x === 'object' && x !== null && typeof (x as any).prepare === 'function'
}

Try / catch

try {
  await payload.db.connect()
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Error: cannot connect to SQLite:')) {
    payload.logger.error('D1 connect failed — check binding name in wrangler.toml and env')
  }
  throw e
}

Prevention

When it happens

Trigger: this.binding is undefined/null (no Cloudflare D1 binding provided); this.binding.withSession is unavailable when readReplicas==='first-primary'; drizzle() fails on a malformed binding; PAYLOAD_DROP_DATABASE dropDatabase fails. Most commonly: missing D1 binding in wrangler/env when running outside a bound worker context.

Common situations: Running Payload locally without a D1 binding (e.g. plain node, missing wrangler --local binding); misnamed binding so this.binding is undefined; production deploy missing the [[d1_databases]] binding; drizzle schema mismatch.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/4868da5e0ce81265. Report an issue: GitHub.