The booking widget on my own consulting site was broken for an unknown length of time. It is the only path by which anyone becomes a client. Nothing was down, nothing threw, nothing appeared in any log, and the analytics that should have shown me the drop were broken by the same root cause.
Here is what happened, because the mechanism is subtle and I suspect I am not the only person shipping it.
The symptom
Load the booking page directly and the Cal.com embed appears exactly as intended.
Load any other page first, click an internal link to the booking page, and the embed never loads. The placeholder sits there indefinitely, cheerfully reading “Calendar loads when you scroll here…” — a message I wrote, promising something that will never happen.
No console error. No failed network request. No exception. The page renders perfectly; it is simply missing the one element that generates revenue.
Why I did not find it sooner
Because I always tested by loading pages directly.
That is worth sitting with. When you build a site, you open localhost:4321/technical-triage and look at it. When you check production, you paste the URL. Every habit you have as the builder loads pages the way search engines and cold visitors do — direct, one at a time.
Real engaged users do the opposite. They land somewhere, read something, and click. The most valuable visitors — the ones who read a case study before deciding to book — took the one path I never tested.
The mechanism
The site uses Astro’s <ViewTransitions /> for client-side navigation. On each navigation it swaps the document body rather than doing a full page load.
That creates an obvious problem: if scripts re-ran on every swap, you would attach duplicate event listeners and re-initialise things that should be initialised once. Astro solves it sensibly. From its swap logic, it keys already-executed inline scripts on their textContent, and marks executed ones with data-astro-exec. A byte-identical inline script never executes again for the remainder of the session.
Now the part that bites. Every inline script in my codebase is byte-identical across pages, because they are all rendered from the same shared Astro components. One CalEmbed.astro, one Analytics.astro, one header component — each emitting exactly the same bytes on every page that uses it.
So after the first page load in a session:
- The Cal.com initialiser never ran again. It sets up an
IntersectionObserverto lazy-load the embed on scroll. No script, no observer, no embed — just the placeholder. - The mobile menu toggle never re-bound. It grabs
#mobile-menu-toggleby id at runtime and attaches a click handler. After a swap that node is detached and replaced; the handler is bound to a corpse. On mobile, where the only persistent CTA lived inside that menu, this meant no route to conversion at all. gtag('config', …)never ran again, so no client-side navigation produced apage_view.
Three independent-looking failures, one cause.
The analytics detail is the nasty part
I want to be specific about why this survived, because it generalises.
GA4 recorded exactly one page_view per session. Every session therefore looked like a single-page session. And a single-page session is also what a healthy direct-landing visit looks like — someone arrives from search, reads, leaves. There is no anomaly to spot.
The instrument that would have detected the failure was disabled by the failure. That is the shape of the defects that survive longest: not the ones that scream, but the ones that break the thing that would have screamed.
The fix
One attribute, three times.
<!-- packages/ui/src/components/CalEmbed.astro -->
<script is:inline data-astro-rerun>
data-astro-rerun opts that script out of the deduplication so Astro re-executes it after every swap. Same change on the header script and the analytics script.
The catch is that your script must now be idempotent, since it genuinely will run on every navigation. Mine already guarded against double-initialisation, but if yours does not, add a check before you add the attribute — otherwise you trade a dead widget for duplicate listeners, which is a worse bug because it is intermittent.
What should have caught it
Three things, in increasing order of value:
A synthetic booking-path test. Navigate from the homepage, click through to the booking page, assert the embed iframe exists. Fifteen lines of Playwright, runs in CI, would have caught this the day it shipped.
An alert on page_view volume. Not on the conversion rate — on the instrumentation. If page views per session drop to exactly 1.0 across every session, something is broken in the measurement layer, and you want to know that independently of what the measurements say.
A rule about how you test. This is the one I actually changed. Any feature that depends on client-side lifecycle gets tested by arriving from somewhere else. Direct loads are the easy path and the unrepresentative one.
The general version
If you use <ViewTransitions /> — or any client-side router that re-evaluates scripts — audit every is:inline script for setup work that must happen per-page. Third-party embeds, IntersectionObserver setup, listeners bound to ids, analytics configuration, anything that queries the DOM at runtime.
The failure mode is not an error. It is silence. And silence is very hard to alert on.
I write up this kind of thing as part of a Technology Health Check — a two-week, fixed-price review with a written report. I also published the full audit of my own site, of which this bug was finding number one. If your product has a conversion path you have never tested by clicking into it, that is a reasonable place to start looking.