Skip to main content

Every dependency policy we review forbids running pre-release software in production. Almost none describe how a pre-release would actually get in. On 29 September 2026, installing Prisma the way Prisma’s own documentation tells you to installs a release candidate. There is no flag, no prompt, no warning, and the command exits zero.

TL;DR

  • Prisma’s npm latest dist-tag points at 8.0.0-rc.17, a release candidate. The companion package @prisma/client points at stable 7.10.0.
  • One documented install command therefore gives you two different major versions of the same ORM, silently, with exit code 0.
  • We pulled the dist-tags of 222 widely used npm packages. Prisma is the only one with a pre-release on latest.
  • Existing projects on ^7.10.0 are unaffected. The exposure is new installs only, which is exactly where nobody is looking.

The latest tag is a pointer, not a promise

A dist-tag is a mutable label a publisher can move to any version at any time. npm’s own documentation is unambiguous about which one matters: “By default, the latest tag is used by npm to identify the current version of a package, and npm install <pkg> (without any @<version> or @<tag> specifier) installs the latest tag.”

The next sentence is the one that matters for risk, and it is the weakest sentence in the document: “Typically, projects only use the latest tag for stable release versions, and use other tags for unstable versions such as pre-releases.”

Typically. That is a description of a custom, not a guarantee, and npm does not enforce it. The documentation then closes the door on any fallback: “Other than latest, no tag has any special significance to npm itself.” The tags that sound like safety rails, next, beta, rc, are decoration. Only latest is load-bearing, and it is load-bearing for everyone who does not name a version.

One command, two major versions

Here is what Prisma’s registry entry looks like today, read straight from the npm registry API:

prisma          latest → 8.0.0-rc.17    next → 8.0.0-rc.10    prev → 7.10.0
@prisma/client  latest → 7.10.0         prev → 6.19.3

Prisma ships as two packages, and the quickstart tells you to install them without version specifiers. So we did, into a clean directory, and read the lockfile:

$ npm install prisma @prisma/client
$ cat package.json
{"dependencies":{"@prisma/client":"^7.10.0","prisma":"^8.0.0-rc.17"}}

A release candidate of version 8 driving a stable client of version 7. One command, no flags, no warning, exit code 0. npm view prisma version agrees that the current version is 8.0.0-rc.17, because from npm’s point of view it is.

Note the ordering, too. next points at 8.0.0-rc.10 from 25 August; latest points at 8.0.0-rc.17 from 24 September. Typing @next gets you something a month more conservative than typing nothing at all.

The dependency surface moves with it. A tree resolved from prisma@^7.10.0 contains 136 packages. The tree you get from a bare npm install prisma contains 454. That is not a patch bump you absorbed, it is a 3.3x increase in the number of third parties in your build, from a command that looks like it asked for nothing in particular.

One caveat, because the obvious follow-up did not say what we expected: the RC tree is not disproportionately full of other pre-releases, at 25 of 454 against 10 of 136 in the stable tree. The problem is the size of the change and the fact that nobody asked for it, not contagion.

222 packages, one anomaly

The obvious question is whether this is simply how the ecosystem works now. It is not. We queried the dist-tags endpoint for 222 widely used packages, spanning frameworks, build tools, test runners, ORMs, drivers and cloud SDKs. All 222 resolved. Exactly one had a pre-release on latest, and it was Prisma.

The rest behave the way the documentation describes, including the ones mid-major-version. Drizzle holds latest at 0.45.3 while its 1.0.0 work sits under beta and rc. Effect keeps latest at 3.22.2 with a 4.0.0 RC line alongside it. One package in 222 means the convention is real and load-bearing, and that your tooling trusts it on every package you have ever installed.

The caret nobody chose

What npm wrote into package.json is not a pin. It is ^8.0.0-rc.17, and semver’s pre-release rules make that range behave in a way most teams will not predict. Resolved against the reference implementation:

^8.0.0-rc.17  vs  8.0.0-rc.16  → false
^8.0.0-rc.17  vs  8.0.0-rc.18  → true
^8.0.0-rc.17  vs  8.0.0-rc.99  → true
^8.0.0-rc.17  vs  8.0.0        → true
^8.0.0-rc.17  vs  8.4.1        → true

So the range you did not choose rides the entire remaining release-candidate train. Every future RC of 8.0.0 is, as far as your lockfile refresh is concerned, a routine in-range update. A CI job that regenerates the lockfile picks up rc.18 the way it would pick up a patch.

Now the same test from the other direction, which is the part worth pinning to a wall:

^7.10.0   vs  8.0.0-rc.17  → false
^8.0.0    vs  8.0.0-rc.17  → false
>=7.0.0   vs  8.0.0-rc.17  → false
*         vs  8.0.0-rc.17  → false

A bare wildcard does not match the release candidate. Neither does ^8.0.0, which is the range you would have written if you had deliberately asked for version 8. Every conventional way of expressing a version range excludes this build. The only way to receive it is to express no preference at all. Being specific about wanting version 8 protects you; being vague does not.

Package managers also disagree about what to write down. pnpm 10.29.3 resolves to the same 8.0.0-rc.17, but records it as an exact pin with no caret. The control probe confirms this is deliberate rather than a quirk of our test: pnpm add lodash writes ^4.18.1, with the caret, for a stable package. pnpm pins pre-releases and floats stable ones. npm floats both. Two teams on the same repository with different package managers will drift apart on the release-candidate line and agree everywhere else.

Who is actually exposed

We tested the case that would make this a five-alarm incident, and it is fine. A project already declaring prisma: "^7.10.0" stays on 7.10.0 through both npm install and npm update. Nothing drags an existing team forward.

That is reassuring, and it is also why this will be found late. The exposure is confined to installs that start from nothing: a new service, a new joiner’s first setup, a Dockerfile running npm install without a committed lockfile, a coding agent asked to add Prisma to a project. All places where an unexplained failure gets blamed on local setup rather than the registry.

The volume is not trivial. In the week to 27 September 2026, prisma was downloaded 19,453,000 times. Version 8.0.0-rc.17 accounted for 198,371 of those, with the whole 8.0.0-rc line at 389,442, or 2.0% of the package’s traffic. Roughly two hundred thousand pulls of a release candidate in seven days, the overwhelming majority of which were nobody’s decision.

What to do about it

None of this needs a policy rewrite. It needs four things your pipeline can enforce.

  1. Fail the build on pre-release versions in the lockfile. Walk package-lock.json, collect every resolved version containing a hyphen, and compare it against a short allowlist you have consciously accepted. A dozen lines, and it catches this on the first CI run.
  2. Commit your lockfile and use npm ci everywhere. The exposure here lives entirely in resolution. If your container build runs npm ci against a committed lockfile, the registry cannot change what you ship between Friday and Monday.
  3. Name the version when you add a dependency. npm install prisma@7 costs one extra character and, as the range tests above show, is strictly safer than saying nothing.
  4. Check the dist-tags of anything mid-major. npm view <pkg> dist-tags takes a second and tells you what the publisher currently means by “current”. Make it part of adding any dependency, not part of debugging one.

Your build is full of defaults chosen by somebody else that are free to change without telling you. We have written about skills files that behave as unpinned dependencies and about an archived storage project sitting in production estates. This is the same shape again: not a vulnerability, not an attack, just a convention holding a load it was never rated for.

Prisma is not doing anything prohibited, and the RC line will presumably become 8.0.0 and resolve the anomaly. That is precisely why it is worth building the check now, while you have a live example to test it against.

REPTILEHAUS builds and maintains production Node and TypeScript estates, and dependency hygiene is part of the DevOps work rather than an audit we sell separately. If you want the lockfile check wired into your pipeline, or a straight answer on what your builds currently resolve, get in touch.

Verified 29 September 2026 against the live npm registry with npm 10.9.4, pnpm 10.29.3 and Node 22.22.0. Dist-tags move; re-run npm view prisma dist-tags before quoting these figures.

📷 Photo by Nick Fewings on Unsplash