Skip to content

Commit 43104e3

Browse files
committed
prevent infinite retry loop for unicode errors
1 parent 3fd8ea8 commit 43104e3

File tree

1 file changed

+72
-20
lines changed

1 file changed

+72
-20
lines changed

apps/webapp/app/v3/eventRepository.server.ts

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,25 +1230,23 @@ export class EventRepository {
12301230

12311231
return events;
12321232
} catch (error) {
1233-
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
1234-
logger.error("Failed to insert events, most likely because of null characters", {
1235-
error: {
1236-
name: error.name,
1237-
message: error.message,
1238-
stack: error.stack,
1239-
clientVersion: error.clientVersion,
1240-
},
1233+
if (isRetriablePrismaError(error)) {
1234+
const isKnownError = error instanceof Prisma.PrismaClientKnownRequestError;
1235+
span.setAttribute("prisma_error_type", isKnownError ? "known" : "unknown");
1236+
1237+
const errorDetails = getPrismaErrorDetails(error);
1238+
if (errorDetails.code) {
1239+
span.setAttribute("prisma_error_code", errorDetails.code);
1240+
}
1241+
1242+
logger.error("Failed to insert events, will attempt bisection", {
1243+
error: errorDetails,
12411244
});
12421245

12431246
if (events.length === 1) {
12441247
logger.debug("Attempting to insert event individually and it failed", {
12451248
event: events[0],
1246-
error: {
1247-
name: error.name,
1248-
message: error.message,
1249-
stack: error.stack,
1250-
clientVersion: error.clientVersion,
1251-
},
1249+
error: errorDetails,
12521250
});
12531251

12541252
span.setAttribute("failed_event_count", 1);
@@ -1258,12 +1256,7 @@ export class EventRepository {
12581256

12591257
if (depth > MAX_FLUSH_DEPTH) {
12601258
logger.error("Failed to insert events, reached maximum depth", {
1261-
error: {
1262-
name: error.name,
1263-
message: error.message,
1264-
stack: error.stack,
1265-
clientVersion: error.clientVersion,
1266-
},
1259+
error: errorDetails,
12671260
depth,
12681261
eventsCount: events.length,
12691262
});
@@ -1917,3 +1910,62 @@ export async function recordRunDebugLog(
19171910
},
19181911
});
19191912
}
1913+
1914+
/**
1915+
* Extracts error details from Prisma errors in a type-safe way.
1916+
* Only includes 'code' property for PrismaClientKnownRequestError.
1917+
*/
1918+
function getPrismaErrorDetails(
1919+
error: Prisma.PrismaClientUnknownRequestError | Prisma.PrismaClientKnownRequestError
1920+
): {
1921+
name: string;
1922+
message: string;
1923+
stack: string | undefined;
1924+
clientVersion: string;
1925+
code?: string;
1926+
} {
1927+
const base = {
1928+
name: error.name,
1929+
message: error.message,
1930+
stack: error.stack,
1931+
clientVersion: error.clientVersion,
1932+
};
1933+
1934+
if (error instanceof Prisma.PrismaClientKnownRequestError) {
1935+
return { ...base, code: error.code };
1936+
}
1937+
1938+
return base;
1939+
}
1940+
1941+
/**
1942+
* Checks if a PrismaClientKnownRequestError is a Unicode/hex escape error.
1943+
*/
1944+
function isUnicodeError(error: Prisma.PrismaClientKnownRequestError): boolean {
1945+
return (
1946+
error.message.includes("lone leading surrogate in hex escape") ||
1947+
error.message.includes("unexpected end of hex escape") ||
1948+
error.message.includes("invalid Unicode") ||
1949+
error.message.includes("invalid escape sequence")
1950+
);
1951+
}
1952+
1953+
/**
1954+
* Determines if a Prisma error should be retried with bisection logic.
1955+
* Returns true for errors that might be resolved by splitting the batch.
1956+
*/
1957+
function isRetriablePrismaError(
1958+
error: unknown
1959+
): error is Prisma.PrismaClientUnknownRequestError | Prisma.PrismaClientKnownRequestError {
1960+
if (error instanceof Prisma.PrismaClientUnknownRequestError) {
1961+
// Always retry unknown errors with bisection
1962+
return true;
1963+
}
1964+
1965+
if (error instanceof Prisma.PrismaClientKnownRequestError) {
1966+
// Only retry known errors if they're Unicode/hex escape related
1967+
return isUnicodeError(error);
1968+
}
1969+
1970+
return false;
1971+
}

0 commit comments

Comments
 (0)