payloadcms/payload · critical · Error

Error: cannot connect to Postgres: ${err.message}

Error message

Error: cannot connect to Postgres: ${err.message}

What it means

Thrown by the Postgres (db-postgres) adapter's `connect` after the underlying connection attempt rejects. The adapter logs the detail, calls `rejectInitializing` to fail the init promise, then re-throws a wrapped Error so that `payload.init()` rejects and the app fails fast rather than running without a DB.

Source

Thrown at packages/db-postgres/src/connect.ts:113

        `${err.message.charAt(0).toUpperCase() + err.message.slice(1)}, creating...`,
      )
      const isCreated = await this.createDatabase()

      if (isCreated && this.connect) {
        await this.connect(options)
        return
      }
    } else {
      this.payload.logger.error({
        err,
        msg: `Error: cannot connect to Postgres. Details: ${err.message}`,
      })
    }

    if (typeof this.rejectInitializing === 'function') {
      this.rejectInitializing()
    }
    throw new Error(`Error: cannot connect to Postgres: ${err.message}`)
  }

  await this.createExtensions()

  await assertOperatorHandlerExtensionsInstalled({
    drizzle: this.drizzle,
    operatorHandlers: this.operatorHandlers,
  })

  // 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)
  }

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Verify the connection string and credentials with `psql` from the same host.
  2. Confirm Postgres is running and reachable: `nc -zv <host> 5432`.
  3. Ensure env vars are actually loaded into the process.
  4. Match SSL settings (`ssl: true`/`sslmode`) to what the server requires.
  5. Confirm the target database exists and the user has CONNECT privileges.

Example fix

// before
payload.init({ secret, db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }) }) // DATABASE_URL unset
// after
payload.init({ secret, db: postgresAdapter({ pool: { connectionString: process.env.DATABASE_URL } }) })
// .env: DATABASE_URL=postgres://user:pass@host:5432/db
Defensive patterns

Strategy: retry

Validate before calling

async function assertPostgresReachable(connectionString, timeoutMs = 5000) {
  const u = new URL(connectionString)
  const ok = await probeTcp(u.hostname, Number(u.port || 5432), timeoutMs)
  if (!ok) throw new Error(`Postgres unreachable at ${u.host}`)
}

Type guard

const looksLikeValidPostgresUrl = (s) => {
  try { const u = new URL(s); return u.protocol === 'postgres:' || u.protocol === 'postgresql:' }
  catch { return false }
}

Try / catch

try { await payload.init() }
catch (e) { if (/cannot connect to Postgres/.test(e.message)) { await backoffRetry(() => payload.init()) } else throw e }

Prevention

When it happens

Trigger: `payload.init()` cannot reach Postgres: invalid connection string/pool config, wrong host/port, bad credentials, the database does not exist, SSL/TLS misconfiguration, or a network/firewall block.

Common situations: Wrong/missing `pool`/`poolURL`/`DATABASE_URL` env var; rotated credentials; Postgres service not running; security group/firewall blocks port 5432; SSL required by server but not configured; DNS resolution failure.

Related errors


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