2026-05-06
Wired PostHog into a Vite-built static site. The init block was gated on import.meta.env.PROD && import.meta.env.VITE_POSTHOG_KEY && import.meta.env.VITE_POSTHOG_HOST, so dev hot-reloads don’t pollute prod data. Pushed the commit, set the two env vars on Railway, watched the live event stream stay empty.
Vite inlines import.meta.env.VITE_* at build time, not runtime. The build that ran on the merge had no VITE_POSTHOG_KEY because the env vars weren’t set yet. import.meta.env.VITE_POSTHOG_KEY resolved to undefined during compile, the gating expression became false && ..., and Rollup statically eliminated the entire init block from the bundle. Setting the env vars on Railway after that does nothing, because the variables don’t exist in the deployed JavaScript to read.
There is nothing to grep for in the live bundle. The PostHog import isn’t there. The init function isn’t there. The block didn’t fail at runtime. It was never present at runtime.
Fix was an empty commit to force a rebuild against the now-populated env. Two-line addition to the deploy runbook: VITE_* env vars must exist on the platform before the build that depends on them. Setting them after the fact requires a new build, not a restart.
Build-time env var inlining decouples “set the variable” from “see the variable.” Frameworks that compile with dead-code elimination make it worse, because the wrong build doesn’t fail loudly. It ships a smaller bundle that silently does less, and the symptom is the absence of behavior rather than an error to chase.