Skip to content

adapter-pg: PgTransaction.performIO called concurrently on single pg Client during update with multiple include relations, triggering deprecation warning #29407

Open

Description

@essenmitsosse

Bug description

When using @prisma/adapter-pg with Prisma 7, the following deprecation warning is emitted at runtime when an update is run, with multiple includes (see below for detailed example and detailed logs)

Calling client.query() when the client is already executing a query is deprecated 
and will be removed in pg@9.0. Use async/await or an external async flow control 
mechanism instead.

I think the Array.map in interpretNode is dispatching multiple performIO calls concurrently onto the same PgTransaction client. Since PgTransaction holds a single pg Client (not a Pool), concurrent client.query() calls on it trigger the deprecation.

This will become a hard error in pg@9.0.

Severity

🔹 Minor: Unexpected behavior, but does not block development

Reproduction

An update with multiple include relations triggers it reliably (see below).

Notes:

  • The warning does not originate from user code — confirmed by full stack trace
  • Passing an explicit pg.Pool with max > 1 to PrismaPg does not resolve it
  • Does not reproduce on findUnique with a similar include shape — appears specific to write operations that open an implicit transaction and then fetch relations on the same connection

Expected vs. Actual Behavior

Expected: - Shouldn't log a warning (will break in next pg major version)
Actual Behavior: - Shows a warning (see below)

Frequency

Consistently reproducible

Does this occur in development or production?

Both development and production

Is this a regression?

Warning occurs since pg 8.20.0 (via Prisma 7.5), but behavior is unchanged on Prisma's side.

Workaround

Downgrade to pg to 8.18

Prisma Schema & Queries

model Parent {
    id         String            @id @default(cuid())
    RelationA  SomeRelationA[]
    RelationB  SomeRelationB[]
    RelationC  SomeRelationC[]
}

model SomeRelationA {
  id        String @id @default(cuid())
  parentId  String
  Parent    Parent @relation(fields: [parentId], references: [id])
}

model SomeRelationB {
// Similar as above
}


model SomeRelationC {
// Similar as above
}
await prisma.parent.update({
    where: { id: args.id },
    data: {},
    include: {
        RelationA: { orderBy: { order: 'asc' } },
        RelationB: { orderBy: { order: 'asc' } },
        RelationC: { orderBy: { order: 'asc' } },
    },
})

Prisma Config

export default {
  schema: path.join('prisma', 'schema'),
  migrations: {
    path: path.join('prisma', 'migrations'),
    seed: 'tsx prisma/seed',
  },
  datasource: DATABASE_URL ? { url: DATABASE_URL } : undefined,
} satisfies PrismaConfig

Logs & Debug Info

Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.
DeprecationWarning: Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.
    at Client.query (/MY_PATH/node_modules/pg/lib/client.js:691:7)
    at PgTransaction.performIO (/MY_PATH/.next/dev/server/chunks/ssr/[root-of-the-server]__044le1t._.js:4548:46)
    at PgTransaction.queryRaw (/MY_PATH/.next/dev/server/chunks/ssr/[root-of-the-server]__044le1t._.js:4500:45)
    at /MY_PATH/node_modules/@prisma/client/runtime/client.js:11:44617
    at xr (/MY_PATH/node_modules/@prisma/client/runtime/client.js:11:26620)
    at #u (/MY_PATH/node_modules/@prisma/client/runtime/client.js:11:47917)
    at e.interpretNode (/MY_PATH/node_modules/@prisma/client/runtime/client.js:11:44586)
    at /MY_PATH/node_modules/@prisma/client/runtime/client.js:11:45739
    at Array.map (<anonymous>)
    at e.interpretNode (/MY_PATH/node_modules/@prisma/client/runtime/client.js:11:45688)
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)

Environment & Setup

  • OS: macOs Tahoe 26.3 (25D125)
  • Database: Postgres 15
  • Node.js version: v24.13.0

Also happens in prod on Vercel with Node 24

Prisma Version

prisma               : 7.5.0
@prisma/client       : 7.5.0
Operating System     : darwin
Architecture         : arm64
Node.js              : v24.13.0
TypeScript           : 6.0.2
Query Compiler       : enabled
PSL                  : @prisma/prisma-schema-wasm 7.5.0-15.280c870be64f457428992c43c1f6d557fab6e29e
Schema Engine        : schema-engine-cli 280c870be64f457428992c43c1f6d557fab6e29e (at node_modules/@prisma/engines/schema-engine-darwin-arm64)
Default Engines Hash : 280c870be64f457428992c43c1f6d557fab6e29e
Studio               : 0.21.1
Preview Features     : typedSql, views

Activity

  1. added
    bug/1-unconfirmedBug should have enough information for reproduction, but confirmation has not happened yet.
    on Mar 30, 2026
  2. added 4 commits that reference this issue on Apr 15, 2026
    3e59137
    dee0259
    fb6172d
    b19be18
  3. added a commit that references this issue on May 20, 2026
    f7ee940
  4. maiconsanson commented on May 30, 2026

    @maiconsanson

    I'm seeing the same warning with:

    • prisma 7.8.0
    • @prisma/client 7.8.0
    • @prisma/adapter-pg 7.8.0
    • pg 8.21.0

    All database access goes through Prisma Client. I don't use pool.connect() or client.query() directly.

    The warning appears intermittently in both development and production:

    (node:25523) DeprecationWarning: Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.

    I'm using PrismaPg with a shared pg.Pool:

    const pool = new Pool({
      connectionString: process.env.SUPABASE_DATABASE_URL,
      connectionTimeoutMillis: 5000,
      idleTimeoutMillis: 10000,
      max: 10,
    })
    
    const prisma = new PrismaClient({
      adapter: new PrismaPg(pool),
    })
  5. Victorcassiano commented on Jul 7, 2026

    @Victorcassiano

    I am facing the same situation as well.

    "prisma": "^7.8.0", "@prisma/client": "^7.8.0", "@prisma/adapter-pg": "^7.8.0", "pg": "^8.21.0",

    2026-07-07 15:33:05.166 [error] (node:4) DeprecationWarning: Calling client.query() when the client is already executing a query is deprecated and will be removed in pg@9.0. Use async/await or an external async flow control mechanism instead.

    ` import { PrismaPg } from "@prisma/adapter-pg";
    import { env } from "@/lib/config/env";
    import { PrismaClient } from "./prisma/generated/prisma/client";

    const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };

    const adapter = new PrismaPg({
    connectionString: env.DATABASE_URL,
    max: 2,
    idleTimeoutMillis: 10_000,
    });

    const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });

    if (process.env.NODE_ENV !== "production") {
    globalForPrisma.prisma = prisma;
    }

    export { prisma }; `

  6. gadcam commented on Aug 11, 2026

    @gadcam

    Also hitting this on 7.9.1 with @prisma/adapter-pg. One data point that widens the scope: it isn't specific to write operations.

    A plain findUnique reproduces reliably when it runs inside an explicit interactive transaction:

    await prisma.$transaction(async (tx) => {
      await tx.trip.findUnique({
        where: { id },
        include: {
          travelerConfig: true, // 3 sibling relations
          computed: true,
          steps: { include: { journeyOptionSet: { include: { journeyOptions: true } } } },
        },
      });
    });

    The warning fires between the call and its resolution — inside the single await — so no userland concurrency is involved.

    The unifying factor looks like "executes on a single dedicated connection" rather than read vs. write:

    • findUnique on the pool → each child query from the join node gets its own pooled connection → no warning
    • write with includes → implicit transaction → one connection → warning
    • read inside an explicit $transaction → one connection → warning

    This lines up with @aqrln's point on #29468 that the constraint is about pg.PoolClient generally rather than transactions specifically — a read inside a transaction reproducing is another sign it tracks the connection, not the operation type.

    Worth noting for anyone else landing here: serializing queries at the Prisma Client level (proxying the tx client) does not suppress this, since the fan-out happens in interpretNode, below the public API.

    Investigated and written with Claude.

  7. added a commit that references this issue on Aug 13, 2026
    d4e3668
  8. Romain-Devillez commented on Aug 21, 2026

    @Romain-Devillez

    Confirming this from a production incident on our side (Prisma 7.9.1, @prisma/adapter-pg 7.9.1, pg 8.22.0), with the exact same stack shape:

    at Client.query (pg/lib/client.js:715:7)
    at PgTransaction.performIO (@prisma/adapter-pg/dist/index.js:654:40)
    at PgTransaction.queryRaw (@prisma/adapter-pg/dist/index.js:604:41)
    at .../client-engine-runtime/src/interpreter/query-interpreter.ts:246:39
        at Array.map (<anonymous>)
    at e.interpretNode (.../client-engine-runtime/src/interpreter/query-interpreter.ts:244:30)
    

    Same trigger: create() with three included relations. A couple of data points that might be useful for #29468:

    • It's specific to writes, as noted above — a findUnique/findMany with the same include shape never reproduces it, since those run through the pool (each relation query gets its own connection) rather than a single transaction client.
    • The pg warning itself only fires from the 3rd concurrently-issued query onward (pg's own check is this._queryQueue.length > 0 before enqueuing — see client.js). We patched a local probe to flag a query issued while another was still active (i.e. the 2nd one, not just the 3rd+), and confirmed a write with only 2 included relations also stacks two concurrent queries on the transaction client — it just stays under pg's current warning threshold. So the practical blast radius of this bug is every write with 2+ relations in include/select, not just 3+; the 3-relation case is simply the first one that becomes visible today. Worth keeping in mind for the fix in fix(adapter-pg): serialize queries within a transaction #29468, since pg@9 turns any concurrency on a busy client into a hard error regardless of count.

    Happy to share more repro details if useful.

  9. gadcam commented on Aug 21, 2026

    @gadcam

    Thanks @Romain-Devillez the _queryQueue.length > 0 detail is a useful confirmation that 2 relations already stack two queries.

    One correction though: it isn't specific to writes.
    A findUnique with the same include shape inside an explicit $transaction reproduces reliably, reads only escape it when they run on the pool, which gives each child query its own connection.

    #29979 fixes it: serialisation moves into the base PgQueryable.performIO, so any single-connection client is covered, while the pool stays parallel.

  10. added a commit that references this issue on Sep 12, 2026
    bcee87e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug/1-unconfirmedBug should have enough information for reproduction, but confirmation has not happened yet.kind/bugA reported bug.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions