Skip to content

Fix: Prisma Enum Not Working — Invalid Enum Value or Enum Not Recognized

FixDevs · (Updated: )

Part of:  JavaScript & TypeScript Errors

Quick Answer

How to fix Prisma enum errors — schema definition, database sync, TypeScript enum type mismatch, filtering by enum, and migrating existing enum values.

The Problem

Prisma throws an error when using an enum value:

const user = await prisma.user.create({
  data: {
    email: '[email protected]',
    role: 'ADMIN',  // Error: Argument role: Invalid value provided. Expected Role, provided String.
  },
});

Or TypeScript complains about the enum type:

import { Role } from '@prisma/client';

const role: Role = 'ADMIN';  // Error: Type '"ADMIN"' is not assignable to type 'Role'

Or an enum query returns no results despite matching data:

const admins = await prisma.user.findMany({
  where: { role: 'ADMIN' },  // Returns empty array — enum filtering broken
});

Or after changing the enum in schema.prisma, the database still uses the old values.

In a deploy pipeline, the worst form of this error appears at migration time: prisma migrate deploy fails because the database enum has values the schema does not (or vice versa), and the deploy is stuck. The blast radius is “every developer waiting for this branch to ship,” and the recovery often requires manually editing the database with ALTER TYPE or marking a migration as resolved with prisma migrate resolve.

Why This Happens

Prisma generates TypeScript types from your schema.prisma file. Enum values are generated as a TypeScript enum object, not plain strings. Several things break this:

  • Using string literals instead of the generated enumrole: 'ADMIN' is a string. Prisma expects role: Role.ADMIN (the generated enum object) or the client-generated string type.
  • Out-of-sync generated client — after changing enum values in schema.prisma, you must run prisma generate to regenerate the TypeScript types. Until then, the old types are in effect.
  • Migration not run — if you added a new enum value, prisma db push or prisma migrate dev must be run to add the value to the database. TypeScript might compile fine, but the database insert will fail.
  • Database-level enum vs Prisma-level — PostgreSQL and MySQL enforce enum values at the database level. Inserting an invalid string causes a database error, not just a TypeScript error.
  • Case sensitivity — Prisma enums are case-sensitive. ADMINadminAdmin.

The deeper structural issue is that Prisma maintains three separate representations of an enum: the schema (enum Role { ... }), the database (a Postgres TYPE or MySQL ENUM column), and the generated TypeScript object. All three must agree. If you edit the schema and forget to run prisma migrate, the database is out of sync. If you run the migration but skip prisma generate, the TypeScript types are stale. If you bypass Prisma and run an ALTER TYPE by hand, both Prisma’s shadow database and migration history will refuse to deploy.

On PostgreSQL specifically, enum values cannot be dropped or renamed inside a single transaction — you have to add new values, migrate data, drop the column, then drop the old type. This is why Prisma migrations involving enum changes often fail in production environments that have automated rollback on migration failure.

Fix 1: Use the Generated Prisma Enum Object

Import and use the generated enum from @prisma/client:

// schema.prisma
enum Role {
  USER
  ADMIN
  MODERATOR
}

model User {
  id   String @id @default(cuid())
  email String @unique
  role  Role   @default(USER)
}
// WRONG — string literal instead of enum
const user = await prisma.user.create({
  data: {
    email: '[email protected]',
    role: 'ADMIN',  // TypeScript error + runtime error
  },
});

// CORRECT — use the generated Prisma enum
import { PrismaClient, Role } from '@prisma/client';

const prisma = new PrismaClient();

const user = await prisma.user.create({
  data: {
    email: '[email protected]',
    role: Role.ADMIN,  // Correct: uses the generated enum
  },
});

Filtering by enum:

// Using enum value in where clause
const admins = await prisma.user.findMany({
  where: { role: Role.ADMIN },
});

// Filter by multiple enum values
const staff = await prisma.user.findMany({
  where: {
    role: { in: [Role.ADMIN, Role.MODERATOR] },
  },
});

// Exclude enum value
const nonAdmins = await prisma.user.findMany({
  where: {
    role: { not: Role.ADMIN },
  },
});

Fix 2: Regenerate the Prisma Client After Schema Changes

After any change to schema.prisma, regenerate:

# Regenerate TypeScript types from schema
npx prisma generate

# Apply schema changes to database AND regenerate client
npx prisma migrate dev --name add-moderator-role

# Or for prototyping without migration history:
npx prisma db push

# Verify the generated types
cat node_modules/.prisma/client/index.d.ts | grep -A5 "enum Role"

Full workflow when adding a new enum value:

// 1. Update schema.prisma
enum Role {
  USER
  ADMIN
  MODERATOR  // ← New value added
  GUEST      // ← Another new value
}
# 2. Create and apply migration
npx prisma migrate dev --name "add-guest-and-moderator-roles"

# 3. Regenerate client (migrate dev does this automatically, but be explicit)
npx prisma generate

# 4. Restart TypeScript server in VS Code if types still look wrong
# Cmd+Shift+P → "TypeScript: Restart TS Server"

Fix 3: Type-Safe Enum Handling in Application Code

Use Prisma’s generated enum type throughout your application:

// services/userService.ts
import { PrismaClient, Role, Prisma } from '@prisma/client';

const prisma = new PrismaClient();

// Function typed with Prisma enum
async function updateUserRole(userId: string, role: Role): Promise<void> {
  await prisma.user.update({
    where: { id: userId },
    data: { role },
  });
}

// Usage — TypeScript enforces valid enum values
await updateUserRole('user-123', Role.ADMIN);    // OK
await updateUserRole('user-123', 'ADMIN');        // TypeScript error
await updateUserRole('user-123', Role.SUPERUSER); // TypeScript error (not in enum)

// Convert string to Prisma enum safely (e.g., from API input)
function parseRole(input: string): Role {
  const valid = Object.values(Role) as string[];
  if (!valid.includes(input)) {
    throw new Error(`Invalid role: ${input}. Valid roles: ${valid.join(', ')}`);
  }
  return input as Role;
}

// In an API handler
app.put('/users/:id/role', async (req, res) => {
  const { role } = req.body;

  let parsedRole: Role;
  try {
    parsedRole = parseRole(role);
  } catch (err) {
    return res.status(400).json({ error: err.message });
  }

  await updateUserRole(req.params.id, parsedRole);
  res.json({ success: true });
});

Fix 4: Migrate Existing Enum Values

When renaming or removing enum values, you need a migration strategy:

// BEFORE
enum Status {
  ACTIVE
  INACTIVE
  PENDING
}

// AFTER — renaming INACTIVE to DISABLED
enum Status {
  ACTIVE
  DISABLED   // Was: INACTIVE
  PENDING
}

PostgreSQL — enum values can’t be renamed directly. Use a manual migration:

-- In a custom migration file:
-- Step 1: Add the new enum value
ALTER TYPE "Status" ADD VALUE 'DISABLED';

-- Step 2: Update existing data
UPDATE "User" SET status = 'DISABLED' WHERE status = 'INACTIVE';

-- Step 3: PostgreSQL doesn't support removing enum values directly
-- You need to recreate the type:
-- This is complex — consider keeping old values for backward compatibility

Simpler approach — keep old values, add new ones, deprecate old:

enum Status {
  ACTIVE
  INACTIVE   // Keep for backward compatibility
  DISABLED   // New preferred value
  PENDING
}
# Just add the new value
npx prisma migrate dev --name "add-disabled-status"

Fix 5: Enums in Raw Queries

When using prisma.$queryRaw, enum values must be cast explicitly:

// PostgreSQL raw query with enum
const users = await prisma.$queryRaw<User[]>`
  SELECT * FROM "User" WHERE role = ${Role.ADMIN}::"Role"
`;
// PostgreSQL requires explicit cast ::"Role" for enum comparison

// Or use Prisma.sql tagged template
import { Prisma } from '@prisma/client';

const role = Role.ADMIN;
const users = await prisma.$queryRaw<User[]>(
  Prisma.sql`SELECT * FROM "User" WHERE role = ${role}::"Role"`
);

MySQL — enums are stored as strings, no cast needed:

// MySQL raw query
const users = await prisma.$queryRaw<User[]>`
  SELECT * FROM User WHERE role = ${Role.ADMIN}
`;

Fix 6: Enum in API Responses and Validation

Serialize enums correctly in API responses and validate input:

// Zod validation with Prisma enum
import { z } from 'zod';
import { Role } from '@prisma/client';

const UpdateRoleSchema = z.object({
  role: z.nativeEnum(Role),  // Validates against Prisma-generated enum
});

// Or build from enum values:
const RoleEnum = z.enum(Object.values(Role) as [string, ...string[]]);

// API handler with validation
app.put('/users/:id/role', async (req, res) => {
  const result = UpdateRoleSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({ errors: result.error.flatten() });
  }

  await prisma.user.update({
    where: { id: req.params.id },
    data: { role: result.data.role },
  });

  res.json({ success: true });
});

Include enum in API response documentation:

// Expose available enum values via API
app.get('/meta/roles', (req, res) => {
  res.json({
    roles: Object.values(Role),
    // Returns: ["USER", "ADMIN", "MODERATOR"]
  });
});

Fix 7: The Stuck Migration Incident Playbook

A deploy that fails on an enum migration is one of the most common production blockers in a Prisma stack. Recovery depends on what state the database is in.

State A: migration applied to DB but Prisma thinks it failed. This happens when the SQL runs but the connection dies before Prisma can mark the migration as applied. Symptom: prisma migrate deploy reports the migration as failed but \d+ "Role" in psql shows the new enum value already exists.

# Tell Prisma the migration is already applied
npx prisma migrate resolve --applied 20260522000000_add_moderator_role

# Verify migration history
npx prisma migrate status

State B: migration partially applied. The SQL inside the migration ran some statements but failed in the middle. PostgreSQL enum changes are particularly risky here because ALTER TYPE ... ADD VALUE cannot be wrapped in a transaction in older Postgres versions, so a partial apply is possible.

-- Inspect current enum values
SELECT enumlabel
FROM pg_enum
JOIN pg_type ON pg_type.oid = pg_enum.enumtypid
WHERE pg_type.typname = 'Role';

-- Manually finish the migration
ALTER TYPE "Role" ADD VALUE IF NOT EXISTS 'MODERATOR';

-- Then mark resolved
npx prisma migrate resolve --applied <migration_name>

State C: migration cannot run at all. Typically because an existing column has a value the new enum does not allow, or a default value references a removed enum member. Diagnose with prisma migrate diff:

# What does Prisma want to do?
npx prisma migrate diff \
  --from-schema-datamodel prisma/schema.prisma \
  --to-schema-datasource prisma/schema.prisma \
  --script

Blast radius and detection. A stuck enum migration blocks every deploy that depends on the affected schema. In a monorepo with shared Prisma client packages, this can lock dozens of services out of deploys until resolution. Monitoring: track prisma migrate status output in CI and alert on Following migrations have not yet been applied for more than 30 minutes — this almost always means a human is currently fighting with prisma migrate resolve.

Recovery. First, never edit a migration file that has already been applied to any environment — Prisma uses checksums and will refuse to deploy if the checksum changes. Second, prefer additive enum changes (add new values) over destructive ones (rename/remove) because additive changes do not require data migration and rarely fail. Third, in PostgreSQL, run enum changes outside transactions when possible:

-- migration.sql
-- The following statement must run OUTSIDE a transaction (Postgres < 12)
ALTER TYPE "Role" ADD VALUE 'GUEST';

Still Not Working?

Prisma client out of date after node_modules reinstall — if you delete node_modules and reinstall, you must run prisma generate again. The generated client in node_modules/.prisma/client is not stored in git:

# Add to package.json scripts for automatic postinstall generation:
{
  "scripts": {
    "postinstall": "prisma generate"
  }
}

SQLite enum behavior — SQLite doesn’t have native enum types. Prisma stores enums as TEXT in SQLite. This means invalid string values won’t cause a database error — only Prisma’s client-side validation will catch them. Always use the generated enum object.

Prisma enum vs TypeScript native enum — don’t mix Prisma-generated enums with TypeScript enum declarations. They look similar but behave differently. Prisma generates a const object { USER: 'USER', ADMIN: 'ADMIN' }, not a TypeScript numeric enum.

Enum value with reserved SQL keyword — if you name an enum value default, select, or any other reserved SQL word, Prisma escapes it correctly in generated queries but raw SQL or external tools may break. Pick non-reserved names.

Old enum value still in use after rename — even after prisma migrate dev shows the rename succeeded, any cached query plans on the database side may reference the old enum OID. Restart connection pools (PgBouncer, the application) after enum migrations to avoid cache lookup failed for type errors.

Sharding and read replicas — if you run Prisma against a sharded or replicated database, an enum migration applied to the primary may not propagate to replicas immediately. Querying a replica during the propagation window can return invalid input value for enum. Pause traffic to replicas, run the migration, then resume.

For related Prisma issues, see Fix: Prisma Transaction Error, Fix: Prisma N+1 Query Problem, Fix: Prisma Migration Failed, and Fix: Prisma Unique Constraint Failed.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles