Skip to content
a@o:~$

cat /blog/phantom-build-failure.md

· 6 min · forensics · next.js · windows · debugging

The phantom build failure: when a capital letter takes down your Next.js build

Every page of a fully static site failed to prerender with an invariant blaming Next.js itself. The framework was innocent. A forensic walkthrough of refuting the obvious suspects until a single character confessed.

symptom

A brand-new Next.js 16 site — fully static, no database, sixteen routes — compiled clean, type-checked clean, then died during prerender. Not one flaky page: every single one, including /_not-found and /_global-error, pages I never wrote.

next build
Error occurred prerendering page "/_not-found".
Error [InvariantError]: Invariant: Expected workStore
to be initialized. This is a bug in Next.js.

Note the last sentence. The error message itself testifies that the framework is at fault. Hold that thought, because the first rule of incident forensics is that confessions volunteered this early are usually misdirection.

evidence

  • The dev server rendered all sixteen routes perfectly. Only the production build died.
  • The failure was total, not partial: framework-generated pages failed alongside mine, which means the trigger sat below my application code.
  • The failing chunk had the same content hash across every experiment that followed — a detail that seemed irrelevant until it wasn't.

refutations

Data-first, refutation-required: every hypothesis earns a controlled experiment, and the experiment's job is to kill the hypothesis, not to flatter it.

  • “My code broke it.” Stashed everything and built the pristine create-next-app scaffold. It failed identically. My code was acquitted.
  • “The Next.js version is buggy.” Downgraded a patch release. Identical failure, identical chunk hash.
  • “The Node runtime is the problem.” The invariant smells like AsyncLocalStorage losing context, which is runtime territory. Switched from Node 20 to Node 24. Identical failure.

Three suspects interrogated, three alibis confirmed. When every reasonable explanation is refuted, the cause is something you have not yet thought to vary.

the tell

Next 16 still ships a webpack escape hatch. Same build through a different bundler — not because webpack would fix anything, but because a second witness describes the same crime differently. Webpack's testimony:

next build --webpack
There are multiple modules with names that only
differ in casing.
 * C:\...\Desktop\Dev\portfolio\node_modules\next\...
 * C:\...\Desktop\dev\portfolio\node_modules\next\...

There it is. Dev and dev. The folder on disk is capitalized; the shell session had been navigating with a lowercase path. Windows' filesystem is case-insensitive, so both spellings resolve to the same bytes — but Node's module registry keys modules by path string, and strings are case-sensitive.

root cause

Every module imported through the lowercase path and the same module imported through the capitalized path became two separate instances. That includes the module holding Next.js's workAsyncStorage — the AsyncLocalStorage that carries render context. The build wrote the store into one copy and read it from the other. The read came back empty, and Next reported, with complete sincerity, a bug in itself.

The invariant was not lying; it was scoped wrong. It was a bug in Next.js's process — injected by a single character of my working directory.

intervention

One line: Set-Location C:\...\Desktop\Dev\portfolio with the exact on-disk casing before building. Sixteen out of sixteen pages prerendered. Total cost of the defect: one capital letter. Total cost of finding it without a method: unbounded.

lessons

  • Error messages assign blame by guessing. Treat “this is a bug in X” as a claim to verify, not a verdict to accept.
  • Reproduce on a pristine baseline before touching your own code. It is the cheapest experiment with the highest information yield.
  • When one tool is opaque, run the same operation through a sibling tool. Different failure renderings of the same fault triangulate the cause.
  • Case-insensitive filesystems with case-sensitive runtimes are a standing hazard on Windows and macOS. If module-identity weirdness appears — duplicated singletons, lost context, double React — audit your path casing first.