The worst outages are not the loud ones. A 500 page is a courtesy — it tells you something died and roughly where. On 8 July, bookasloth.com served a perfectly valid HTML document, 200 OK, with nothing in it. Every route. White.
The console was clean. That is the detail worth sitting with. Not "clean apart from one thing" — clean. Whatever killed the app did it before anything existed to report it.
What actually happened
We had just moved deploys into a GitHub Actions pipeline. Reasonable idea: build the client in CI, ship the finished artefact. The commands were the ones the docs give you — pull the environment, build with it, deploy the prebuilt output.
Environment variables marked Sensitive in Vercel are never downloadable. Not by that pull, not by anything. They exist only inside Vercel's own build infrastructure.
So CI pulled the environment, got back a version with the sensitive values quietly missing, and built a bundle against it. No warning. The build went green. What it produced was a JavaScript bundle where every one of our client environment variables was undefined — including the database URL.
The entry module threw `supabaseUrl is required` on the first line that ran. React never mounted. There was no app yet to catch the error, no boundary, no logger, nothing to write to the console. The page stayed exactly as the HTML shipped it: empty.
A build that cannot see its secrets does not fail. It succeeds, and ships an application with no secrets in it.
Why it took a while to see
Because every instinct was wrong. You check the server — server is fine, API responding. You check DNS — fine. You check the deploy — green tick, Ready. You check the console — empty. Every dashboard we own said the system was healthy, and the system was serving a white rectangle to every human in the country.
The thing that broke it open was re-importing the deployed entry module by hand, in the page. The browser caches the module's rejection, so importing it again hands you back the exact error the app died on, long after the moment it happened. `supabaseUrl is required`. Fifteen seconds of certainty after a long time without any.
The other useful trick was cruder: search the deployed bundle for our database reference. If it is not in there, the environment did not make it into the build, and you can stop theorising.
The fix, and the guard
The deploy now builds remotely, where the sensitive variables actually live, instead of trying to smuggle them into CI. That resolved it the same day.
But fixing the deploy only fixes the deploy. The real problem was that a build could complete without the things it needs to run. So the Vite config now hard-fails a production build with a missing database URL, key, or API URL. It is about six lines. It converts a silent white screen into a loud red build failure, which is the trade every time.
- 01A build missing critical env vars must fail at build time, not at boot. Anything else is a white screen waiting for a Tuesday.
- 02Green does not mean correct. It means nothing threw. Those are different claims.
- 03When every dashboard says healthy and users say broken, the users are right and you are measuring the wrong thing.
The landmine we left behind
One thing worth knowing if you ever roll back on Vercel: an Instant Rollback pins production. Deploys after it go Ready, sit there with a little clock icon, and do not go live. They look shipped. They are not. You have to promote the deployment by hand.
We know this because we spent a stretch of the same evening confident we had already fixed something that no user could see.
Total downtime was a single day, on a platform young enough that the number of people affected is small enough to be embarrassing rather than catastrophic. That is the one genuine advantage of breaking things early: the blast radius is mostly your own dignity.
Common questions
What causes a React app to render a blank white page with no console errors?
Usually a module-level throw before React mounts — often a missing environment variable in the bundle. Nothing has initialised yet, so nothing exists to report the error. Re-import the entry module in the console to retrieve the cached rejection.
Why were the environment variables missing from the build?
Variables marked Sensitive in Vercel cannot be downloaded to a CI machine — they exist only inside Vercel's build infrastructure. A CI build pulls the environment, silently receives no values for them, and builds a bundle where they are undefined.
How do you stop it happening again?
Fail the production build when required env vars are absent, rather than letting it succeed and shipping an app that cannot boot.
by Bolt 958
Want this for your own bookings?
Set up your page, sync your calendar, and start getting paid at 0% commission.
Become a host