Skip to content

Prisma types are wrong after a schema edit: generated client drift

The Prisma client is generated code. Edit the schema without regenerating and TypeScript describes the old database, locally, in CI-free builds and after a cached deploy.

Prisma4 min readships at docs/solutions/prisma/client-drift-after-schema-edit.md

Tags: prisma · codegen · typescript · vercel · troubleshooting

You add a field to schema.prisma, use it, and TypeScript refuses:

Object literal may only specify known properties,
and 'archivedAt' does not exist in type 'ProjectSelect'.

Or the mirror image, at runtime, in a deploy that built without complaint:

PrismaClientValidationError:
Unknown argument `archivedAt`. Available options are marked with ?.

Or the strangest version: the code compiles, the query runs, and the column simply is not in the result object.

None of these are bugs in your code. They are all the same thing: the generated client no longer matches the schema.

Why there is a generated client at all

The typed prisma.project.findMany with your exact models, your exact fields and the ProjectSelect type the error is complaining about is not in any npm package. prisma generate writes it from schema.prisma. Since Prisma 7 the prisma-client generator writes plain TypeScript to the output folder the generator block names, in this repo src/generated/prisma, and you import it from there:

import type { Project } from "@/generated/prisma/client";

@prisma/client is still a dependency, but only as the runtime that generated code imports. It exports no models.

That has one consequence worth internalising: the client is build output, not a dependency. It is derived from schema.prisma the way a compiled bundle is derived from source. If the source changes and the build does not re-run, you are looking at stale output. This repo git-ignores the folder, so a fresh checkout has no client at all until generate runs.

The fix, locally

bun run db:generate

Then restart the TypeScript server. This is the step people miss: the editor's language server holds the old types in memory and will keep showing the error after the files on disk are correct. Restart next dev too, since its module cache has the old client loaded.

bun run db:migrate runs generate before it migrates, which is why this rarely bites during a normal migration workflow. (Prisma 7's migrate dev no longer generates on its own; the script does it.) It bites when you edit the schema and don't migrate: a @@map, an @@index, a comment, a formatting pass, a change you made intending to migrate later.

The four situations that produce drift

1. Edited the schema, ran nothing. The common one. Generate.

2. Pulled someone else's schema change. Their migration is in your working tree, your generated client is not. This repo has a postinstall that runs generate, but whether bun install fires it depends on your package manager and on whether anything actually changed. After any pull that touches prisma/, run generate.

3. A deploy restored a cached install. Vercel caches dependencies between builds. If the cache is a hit, the install can be a no-op and postinstall may not run, so the build finds no src/generated/prisma at all (Module not found: Can't resolve '@/generated/prisma/client') or, on a host that keeps the working directory, a client generated from an older schema. This is why the build command must be explicit even though postinstall exists:

bun run db:generate && bun run db:migrate:deploy && bun run build

4. Code still imports @prisma/client. Anything written for Prisma 6 (a snippet, an older library, a copied helper) imports models from @prisma/client. Under Prisma 7 that package has no generated models, so the import fails to typecheck, and at runtime it cannot find .prisma/client/default, even right after a generate. Import from @/generated/prisma/client instead.

Client drift vs database drift

Two different problems with similar names, worth separating:

  • Client drift. Generated code is older than schema.prisma. Symptom: TypeScript errors, or Unknown argument at runtime. Fix: prisma generate.
  • Database drift. The database is not what the migration history says it should be, usually because someone ran db push or changed a table by hand. Symptom: prisma migrate status reports drift, or a query fails with column ... does not exist even though the client knows about it. Fix: reconcile with a new migration; reset only on a local database.

The tell is which side is stale. If TypeScript knows about the field and Postgres does not, that is database drift. If Postgres knows and TypeScript does not, that is client drift.

Making it not happen again

Generate on install. Already wired: postinstall runs prisma generate, so every clone, every dependency change and every fresh CI-free environment gets a correct client without anyone remembering.

Generate in the build command, as above. postinstall does not always fire on a cache hit, so belt and braces.

Do not commit the generated client. It is build output; committing it guarantees stale diffs and merge conflicts. This repo lists /src/generated/prisma/ in .gitignore, and Biome skips it for the same reason.

Treat a schema edit as a two-command action. Edit, then migrate (or generate). A schema edit alone is an unfinished change, in the same way that editing a .proto without regenerating is.

The one-minute triage

When the types look wrong, in order:

bunx prisma validate            # is the schema even valid?
bun run db:generate              # regenerate; read the output path it prints
bunx prisma migrate status      # is the database behind the history?

Then restart the TS server and the dev server. If the error survives all four, look at the import: a file importing from @prisma/client, or from a generated folder other than the one schema.prisma names, is reading types nothing regenerates.