I ran the test suite on my site this morning before touching anything. It reported 305 failed out of 979. Yesterday the same commit was green, nothing had been pushed overnight, and git status was clean. Every failure said the same thing: React.act is not a function. That is not a message about my code. React picks which build to load at require time, from NODE_ENV, and the production build does not export act. So the question was not what broke. It was who set NODE_ENV to production.
node -e 'console.log(process.env.NODE_ENV)'
env -u NODE_ENV npx vitest runIt was my own shell. Not the repo, not a dependency bump, not a lockfile. A parent process had exported NODE_ENV=production and the suite inherited it. With the variable removed, the same commit passed 1117 of 1117.
| NODE_ENV of the caller | Passed | Failed | Uncollected |
|---|---|---|---|
| production | 661 | 305 | 0 |
| unset | 1117 | 0 | 0 |
| production, after fixing React only | 968 | 0 | 18 |
| anything, after fixing the config | 1119 | 0 | 0 |
- Incident
- A suite that passed yesterday reported 305 failures on an unchanged commit, all of them React.act is not a function.
- Decision
- I fixed no test. I pinned NODE_ENV in the test config, both before the config loads and for the workers, then added two assertions that fail if either pin is removed. Row three is why both are needed: pinning it for the workers fixed React and left 18 files failing to collect, because the build tool reads the variable separately, in the config process, before any worker exists. Two consumers, one variable, and fixing the loud one hid the quiet one.
- Portable lesson
- A gate whose result depends on the environment of whoever ran it is not a gate. Pin the variable in the config, where every caller gets it.