Why Old Browser Tabs Request Deleted JavaScript Chunks After a Deployment
A deployment that deletes old assets immediately breaks every browser tab that was opened before the release. For any app where users keep tabs open for hours, this turns a routine deploy into a stream of support tickets, and a blind refresh-on-error strategy masks the real failure while discarding unsaved user work.
When a single-page app uses route-based lazy loading, a user who never refreshes their tab is still running an old JavaScript entry point. After a new deployment replaces the build directory, that stale entry script continues to request the old hashed chunk filenames it knows about, which no longer exist on the server, producing a 404 and a `Failed to fetch dynamically imported module` error. A refresh fixes it because the browser then fetches the new HTML and the new entry script with updated chunk references.
The failure spans three layers: the deployment pipeline that deletes old assets, the caching strategy that determines how long HTML and hashed resources live, and the client-side recovery logic. Simply forcing a refresh on error hides the problem without revealing whether the deployment process is cutting off active sessions. Retaining old hashed assets for a transition window lets stale tabs finish loading, but it does not solve API incompatibility between old frontend code and new backend interfaces.
A reliable investigation starts with the Network tab: confirm the requested URL, the status code, and the initiator script. If the initiator is an old entry script and the requested hash no longer exists on the server, the deployment pipeline has broken the contract with running clients. The fix is a combination of deployment-side retention, correct `Cache-Control` headers that treat HTML as volatile and hashed assets as immutable, and a measured client-side recovery flow that logs the failure before refreshing, with safeguards against infinite loops and data loss.
The core contract violation is between the deployment process and the running client: the server deletes assets that the client was explicitly told it could request later. Treating deployments as atomic directory swaps rather than incremental additions breaks the lazy-loading model.
Refresh-on-error is a recovery mechanism, not a root-cause fix. It hides the deployment pipeline's behavior and can mask a misconfigured build path or a CDN propagation delay that a simple reload won't cure.
The split caching strategy for HTML versus hashed assets is widely recommended but often implemented backwards in practice, where HTML gets cached aggressively and hashed files get short TTLs, creating exactly the stale-entry-script problem described here.
API versioning and static asset retention are usually managed by different teams, but a deployment that keeps old chunks while changing API response shapes still breaks the user experience. The two layers must be coordinated.