Skip to content

Commit 47cba16

Browse files
fix: handle DriverAdapterError for transaction write conflict retries
With Prisma 7's PrismaPg driver adapter, write conflicts (PostgreSQL 40001) surface as DriverAdapterError with message 'TransactionWriteConflict' instead of PrismaClientKnownRequestError with code P2034. Without this fix, the $transaction retry logic silently stops retrying on serialization failures. Added isDriverAdapterTransactionWriteConflict() check to isPrismaRetriableError() and updated $transaction catch block to use the unified retriable check. Co-Authored-By: Eric Allam <eallam@icloud.com>
1 parent 39c62b4 commit 47cba16

File tree

1 file changed

+25
-8
lines changed

1 file changed

+25
-8
lines changed

internal-packages/database/src/transaction.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,23 @@ export function isPrismaKnownError(error: unknown): error is PrismaClientKnownRe
3737
*/
3838
const retryCodes = ["P2024", "P2028", "P2034"];
3939

40+
/**
41+
* With Prisma 7's driver adapter (PrismaPg), write conflicts (PostgreSQL 40001)
42+
* surface as a DriverAdapterError with message "TransactionWriteConflict" instead
43+
* of a PrismaClientKnownRequestError with code P2034. This function detects that
44+
* error so the retry logic still works.
45+
*/
46+
function isDriverAdapterTransactionWriteConflict(error: unknown): boolean {
47+
if (typeof error !== "object" || error === null) return false;
48+
const err = error as { name?: string; message?: string };
49+
return err.name === "DriverAdapterError" && err.message === "TransactionWriteConflict";
50+
}
51+
4052
export function isPrismaRetriableError(error: unknown): boolean {
53+
if (isDriverAdapterTransactionWriteConflict(error)) {
54+
return true;
55+
}
56+
4157
if (!isPrismaKnownError(error)) {
4258
return false;
4359
}
@@ -90,15 +106,16 @@ export async function $transaction<R>(
90106
try {
91107
return await (prisma as PrismaClient).$transaction(fn, options);
92108
} catch (error) {
93-
if (isPrismaKnownError(error)) {
94-
if (
95-
retryCodes.includes(error.code) &&
96-
typeof options?.maxRetries === "number" &&
97-
attempt < options.maxRetries
98-
) {
99-
return $transaction(prisma, fn, prismaError, options, attempt + 1);
100-
}
109+
// With Prisma 7 driver adapters, write conflicts (PG 40001) surface as
110+
// DriverAdapterError instead of PrismaClientKnownRequestError P2034.
111+
// Check for both error shapes so retries work correctly.
112+
const retriable = isPrismaRetriableError(error);
113+
114+
if (retriable && typeof options?.maxRetries === "number" && attempt < options.maxRetries) {
115+
return $transaction(prisma, fn, prismaError, options, attempt + 1);
116+
}
101117

118+
if (isPrismaKnownError(error)) {
102119
prismaError(error);
103120

104121
if (options?.swallowPrismaErrors) {

0 commit comments

Comments
 (0)