Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

db-postgres: connect() rejects its `initializing` promise with no argument, producing an unhandledRejection with reason `undefined`

Open Beginner friendly
#18,656 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@Metehan-Bicer is already working on this.

Since Oct 10, 2026.

  • #18658 by @Metehan-Bicer — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
node.js, postgresql, typescript
Domain
backend, database

Research direction

Read packages/db-postgres/src/index.ts and packages/db-postgres/src/connect.ts, then check the rejectInitializing type in packages/drizzle/src/types.ts and the consumer in packages/drizzle/src/transactions/beginTransaction.ts. The issue describes two possible fixes; trace how connection failures reach the promise and how beginTransaction handles rejection. Done means the orphan rejection no longer reports undefined while connection failures still surface.

Written by the indexing model from the issue text.

Description

area: db-postgres Bug
Describe the Bug

@payloadcms/db-postgres rejects its internal initializing promise with no argument when connect() fails. Because the only thing that ever awaits that promise is beginTransaction, a failed connect on a request path that opens no transaction leaves the rejection unhandled — and since the reason is undefined, Node attaches no stack to it.

The result is an unhandledRejection with reason === undefined. In an app with Sentry (or any unhandledRejection reporter) wired up, that arrives as an event with no message, no stack and no in-app frames, pointing only at the reporter's own handler. It is effectively undebuggable from the report: nothing in it names Payload, Postgres, or the connection at all.

The two lines, at v3.90.2:

  • packages/db-postgres/src/index.ts#L80-L82 — the promise is created and no handler is attached:

    const initializing = new Promise<void>((res, rej) => {
      resolveInitializing = res
      rejectInitializing = rej
    })
    
  • packages/db-postgres/src/connect.ts#L109-L111 — it is rejected with nothing, and then a separate real Error is thrown:

    if (typeof this.rejectInitializing === 'function') {
      this.rejectInitializing()          // ← no argument ⇒ rejects with `undefined`
    }
    throw new Error(`Error: cannot connect to Postgres: ${err.message}`)
    

Still present on main (packages/db-postgres/src/connect.ts, this.rejectInitializing() immediately before the throw). The type itself guarantees it can never carry a reason — rejectInitializing: () => void, declared with no parameter, in packages/drizzle/src/types.ts and the db-postgres types.

The only consumer is packages/drizzle/src/transactions/beginTransaction.ts (await this.initializing). packages/drizzle/src/destroy.ts replaces the promise rather than awaiting it. So on any read path that does not begin a transaction, nothing is attached when the rejection lands.

Two suggested fixes, either of which is sufficient:

  1. Pass the error: this.rejectInitializing(err), and widen the type to (err?: unknown) => void. The rejection then at least carries a stack for whoever is listening.
  2. Attach a no-op handler where the promise is created, so the orphan can never surface: initializing.catch(() => {}). connect() already throws the real error separately, and beginTransaction's own await still rejects, so nothing is swallowed.

(1) is the more useful of the two; doing both costs nothing.

Link to the code that reproduces this issue

https://github.com/payloadcms/payload/blob/v3.90.2/packages/db-postgres/src/connect.ts#L109-L111

I have not pushed a create-payload-app scaffold for this one, because the defect is visible in the published source above and does not depend on app config: rejectInitializing is typed () => void, it is called with no argument, and the promise it rejects has no handler attached at creation. Happy to put a scaffold together if you'd rather have one to run — say so and I will.

Reproduction Steps

Any postgresAdapter app where connect() fails will do it. Forcing it deterministically:

  1. Build the app and start it (output: standalone in my case, but plain next start behaves the same).

  2. Set connectionTimeoutMillis on the adapter's pool low enough that the pool's timer expires during the first server render, which blocks the event loop while the CMS config is built. With connectionTimeoutMillis: 1 through 10 it fires every time:

    db: postgresAdapter({
      pool: { connectionString: process.env.DATABASE_URL, connectionTimeoutMillis: 1 },
    })
    
  3. Request any page that reads from the database but opens no transaction (a plain payload.find() in a server component is enough).

  4. Observe two things in the output: Payload's own Error: cannot connect to Postgres: Connection terminated due to connection timeout — correct and useful — and, separately, an unhandledRejection whose reason is undefined.

With a listener installed to inspect it:

process.on('unhandledRejection', (reason) => {
  console.log('typeof reason:', typeof reason, '| isError:', reason instanceof Error)
})
// → typeof reason: undefined | isError: false

Patching the reject capability of new Promise(executor) to log a stack when called with a non-Error names the call site directly:

!! new Promise(…) reject() with a non-Error reason: undefined
   at Object.rejectInitializing (…)
   at Object.i [as connect]  (…/connect.js)
   at async …init            (…)

Ruled out below Payload, by reading the installed source, so that the report lands in the right place:

  • [email protected] can only reject its connect promise with a truthy value — err ? rej(err) : res(client).
  • [email protected]'s Client.end() with no callback returns a promise with no reject path at all.
  • @opentelemetry/[email protected]'s handleConnectResult re-rejects the identical error.
Which area(s) are affected?

area: db-postgres

Environment Info
payload:              3.90.2
@payloadcms/db-postgres: 3.90.2
@payloadcms/drizzle:  3.90.2
pg:                   8.20.0
pg-pool:              3.13.0
next:                 15.4.11
node:                 22.16.0 (also reproduced on 22.23.1)
Dominant language
TypeScript
Stars
45.3k
Forks
4.3k
Avg merge
1d 21h
Merged PRs (30d)
272

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.