Přeskočit na obsah

Prisma enum drift — runtime ALTER TYPE bez updatu schema.prisma

import { Aside } from ‘@astrojs/starlight/components’;

Endpoint nebo stránka náhodně padá pro některé uživatele:

  • API vrací HTTP 500
  • UI ukazuje generický toast typu „Nepodařilo se načíst data”
  • v logu se opakovaně objevuje PrismaClientUnknownRequestError: Value 'X' not found in enum 'Y'
  • bug se „opraví” a po několika dnech / po dalším deployi je zpět — typicky po prisma generate v rámci čistého buildu

Někde v aplikaci (často self-healing cron / lifecycle worker) běží idempotentní raw SQL:

ALTER TYPE "MyEnum" ADD VALUE IF NOT EXISTS 'NEW_STATE';

A vzápětí se do sloupce zapíše 'NEW_STATE'. PostgreSQL je v pohodě.

Jenže schema.prisma o té hodnotě neví:

enum MyEnum {
OLD_STATE_A
OLD_STATE_B
// NEW_STATE chybí
}

Vygenerovaný Prisma client tedy zná jen OLD_STATE_A | OLD_STATE_B. Při čtení řádku, kde už je 'NEW_STATE', runtime neumí enum deserializovat a hodí:

PrismaClientUnknownRequestError:
Invalid `prisma.myTable.findUnique()` invocation:
Value 'NEW_STATE' not found in enum 'MyEnum'

Endpoint to zachytí jako 500, frontend zobrazí generickou hlášku. Každý prisma generate (typicky v rámci pnpm build nebo CI) tu chybu znovu „obnoví” ze zastaralého schématu — což vysvětluje, proč to vypadá jako „opravoval jsem to už několikrát”.

Dorovnat schema.prisma k tomu, co je už v DB, a vygenerovat klient. DB samotnou není potřeba migrovat (hodnota tam je).

Před:

enum MyEnum {
OLD_STATE_A
OLD_STATE_B
}

Po:

enum MyEnum {
OLD_STATE_A
NEW_STATE
OLD_STATE_B
}
Terminál
pnpm prisma generate # nebo db:generate v monorepu
# čistý build, restart procesu

Funguje to proto, že prisma generate produkuje TypeScript definice + runtime mapování enumu z hodnot v schema.prisma. Jakmile tam hodnota je, deserializace projde.

Jak se tomu vyvarovat v jiných systémech

Sekce “Jak se tomu vyvarovat v jiných systémech”
  • Detection: v rámci grep -rn 'ALTER TYPE.*ADD VALUE' src/ najít všechny runtime mutace enumů a křížově ověřit s schema.prisma. V CI dá smysl skript, který načte pg_enum z testovací DB a srovná s vygenerovaným klientem.
  • Anti-pattern: „dočasně” přidat hodnotu ALTER TYPE-em jen v cron handleru s tím, že schema.prisma se „dotáhne později”. Nikdy se nedotáhne a každý regenerate to maskuje, dokud někomu v produkci nezačnou padat readlines.
  • Lepší přístup: Enum hodnoty patří do migration / schema.prisma jako každá jiná schema změna. Runtime ALTER mít maximálně jako idempotentní bootstrap pro dev/preview prostředí, kde uživatel nemusí pamatovat na prisma migrate. Produkce by měla mít hodnotu už v migrations.

Sister bugs / související

Sekce “Sister bugs / související”
  • Stejný princip: column drift, kde aplikace přidá sloupec ALTER TABLE ... ADD COLUMN IF NOT EXISTS, ale Prisma o něm neví — zde to nepadá tak hlasitě (column zmizí z SELECT * výsledků), takže bug může roky tichounce ujídat data.
  • NextAuth JWT a session: pokud má enum v sobě roli a JWT cachuje starou hodnotu, po přidání nové role uživatelé s tou rolí najednou nemůžou nic, dokud se JWT neobnoví.
Přidal aiarchitekt.cz · 1. 6. 2026 2:00
Provozuje aiarchitekt.cz