What happens
SlotAllocationService.guardInitialAllocationInTx (utils/slotAllocation/SlotAllocationService.ts ~L759-765, added by #998) takes the per-event advisory lock with
await tx.$queryRaw`SELECT pg_advisory_xact_lock(hashtextextended(${key}, 42))`;
pg_advisory_xact_lock returns void. Under Prisma 7's driver adapter the result row carries a column of Postgres type void, which the adapter cannot deserialise, so the call throws PrismaClientKnownRequestError: Failed to deserialize column of type 'void' before the allocation begins. Every path that reaches the guard fails: manualAllocate from the request calendar (~L1567) and the reschedule allocation (POST /api/appointments/[appointmentId]/reschedule).
Evidence
Sentry FAMILIARISE_WEB-2W, 2 events on 2026-09-06 (~02:00Z) from the dev branch deploy, trace through manualAllocate → guardInitialAllocationInTx. The same code is on prod (git show origin/prod:utils/slotAllocation/SlotAllocationService.ts carries the call), so production manual allocation and reschedule are affected too. Wave 1–3 E2E did not exercise these paths (checkout with direct slots does not take this guard), which is why it went unnoticed.
Fix
Take the lock through a statement whose result Prisma does not deserialise, or give it a deserialisable shape: await tx.$executeRaw\SELECT pg_advisory_xact_lock(hashtextextended(${key}, 42))`(the common Prisma idiom for advisory locks), orSELECT pg_advisory_xact_lock(...) IS NULL(boolean). Keep the lock inside the transaction and the key unchanged. Grep the repo for any other$queryRawthat selects avoid-returning function (pg_advisory_lock, pg_notify, set_config` with void results) and fix them the same way. One pin: the guard issues the lock through the chosen call and allocation proceeds; a test that runs the raw statement against the test database if one exists, else a source-level pin plus a Prisma-mock expectation.
Found by the Sentry poller on 2026-09-06.
What happens
SlotAllocationService.guardInitialAllocationInTx(utils/slotAllocation/SlotAllocationService.ts~L759-765, added by #998) takes the per-event advisory lock withpg_advisory_xact_lockreturnsvoid. Under Prisma 7's driver adapter the result row carries a column of Postgres typevoid, which the adapter cannot deserialise, so the call throwsPrismaClientKnownRequestError: Failed to deserialize column of type 'void'before the allocation begins. Every path that reaches the guard fails:manualAllocatefrom the request calendar (~L1567) and the reschedule allocation (POST /api/appointments/[appointmentId]/reschedule).Evidence
Sentry FAMILIARISE_WEB-2W, 2 events on 2026-09-06 (~02:00Z) from the
devbranch deploy, trace throughmanualAllocate → guardInitialAllocationInTx. The same code is onprod(git show origin/prod:utils/slotAllocation/SlotAllocationService.tscarries the call), so production manual allocation and reschedule are affected too. Wave 1–3 E2E did not exercise these paths (checkout with direct slots does not take this guard), which is why it went unnoticed.Fix
Take the lock through a statement whose result Prisma does not deserialise, or give it a deserialisable shape:
await tx.$executeRaw\SELECT pg_advisory_xact_lock(hashtextextended(${key}, 42))`(the common Prisma idiom for advisory locks), orSELECT pg_advisory_xact_lock(...) IS NULL(boolean). Keep the lock inside the transaction and the key unchanged. Grep the repo for any other$queryRawthat selects avoid-returning function (pg_advisory_lock,pg_notify,set_config` with void results) and fix them the same way. One pin: the guard issues the lock through the chosen call and allocation proceeds; a test that runs the raw statement against the test database if one exists, else a source-level pin plus a Prisma-mock expectation.Found by the Sentry poller on 2026-09-06.