This note started as a list of six bugs. I checked the list against the code before publishing, because the note argues you should never state what an artifact contains without looking at it, and it seemed cheap to be wrong about that in a note about that.
Three of the six weren’t true.
One blamed a crop bug on an image component the project has never installed. The real bug was a social platform centre-cropping a server-rendered card. One described an analytics gap that happened in a different repository. One asserted that documentation disagreed with shipped behaviour; the code says nothing about documentation, and the current docs agree with the behaviour exactly. I had reconstructed a plausible cause and remembered it as a finding.
So before the list, the thing the list is about: my memory of the artifact was wrong about the artifact. Same failure mode, one level up.
Here are the three that survived.
The certificate parser that worked on my machine. Local dev runs Node 26. The Vercel function runtime is Node 22. X509Certificate.signatureAlgorithm exists on the first and not the second, so on the deploy target it was permanently undefined and the SHA-1 check never fired. No error, no throw. Scan reported on a check it had stopped performing. The fix reads the OID out of the certificate DER by hand, which behaves identically on both. Verified against a known SHA-1 intermediate on 22.22 and 26.3.
serverAssets and the relative path. The OG renderer needs fonts bundled into the serverless function, since it can’t fetch them at request time. The config named the directory as a relative string. Nitro resolves a relative serverAssets directory against its own srcDir, not the project root, so it pointed at a path that doesn’t exist and bundled nothing. In dev the fonts were on disk anyway and everything worked. In production the storage read returned null and OG rendering 500’d on every request. A relative path that bundles nothing still typechecks. It still works locally. The only way to see it is an empty _assets map in the build output.
Upstash credentials under the wrong names. Configuring Upstash directly gives you UPSTASH_REDIS_REST_*. Vercel’s marketplace integration injects KV_REST_API_*, legacy names it kept for compatibility. Both pairs were present in the environment. Neither matched what the client read. Every publish failed with “Redis isn’t configured,” which was true, in a way I couldn’t see from the code.
Three instances, one shape. The runtime differs, the filesystem root differs, the environment variable names differ. In every case the source was correct and the tests were correct, and both were describing something other than the thing that shipped.
I test the source and I deploy the build. Those are different objects. The suite tells me my source behaved correctly under my conditions, and makes no claim at all about the bundle.
What catches these is looking at the artifact. Reading the build output, hitting the deployed endpoint, confirming a value arrived rather than confirming a request was accepted. Not reasoning about what the build should contain from the code that produced it. That reasoning is how I got three wrong entries into a list of six.
A passing test suite proves your tests pass where you ran them. It says nothing about what the shipped artifact contains, and neither does your memory of it.