← All posts
January 20, 2026 · backend

Prisma Decimal is not a number: the NaN bug that reached production

Money code has no tolerance for surprises, so when PlayZeet's bet-settlement job started writing amount: NaN, it went straight to the top of the list.

The trap

Prisma returns Decimal objects for decimal columns, not native numbers. The moment you do arithmetic like a + b on two Decimal values — or mix a Decimal with a number — you get results that silently coerce to NaN instead of the value you expected.

The fix

Normalise at the boundary. The instant a Decimal leaves the database layer, convert it explicitly, and never compare Decimal instances with ===.

import { Prisma } from "@prisma/client";

const toNum = (d: Prisma.Decimal | number) =>
  typeof d === "number" ? d : d.toNumber();

const total = toNum(bet.stake) + toNum(bet.bonus);

The lesson: treat the ORM's return types as a contract, not a formality — especially when real money is on the line.