Repository navigation
Description
Describe the bug
On the server, a __loadRemote__ wrapper is evaluated once and makes a single load attempt, exported as __mf_remote_pending (getEagerRemotePendingExport). If that attempt fails, every later import of the remote gets the same rejected promise for the life of the process. This happens, for example, when the host serves its first request while a remote is still deploying. The host never server-renders that remote again, even after the remote is back; only a restart helps.
In development the failure is cached a second time. Server wrappers export then (getServerThenExport), so Vite's module runner adopts the first load's rejection into the wrapper's cached evaluation promise, and every later import() of the wrapper rejects with it.
Version
@module-federation/vite 1.23.2 and main (9fd4193), Vite 8.3.2, Node 24 and 26.
Reproduction
- Start a host that imports a remote on the server (here, from a TanStack Start route loader) while the remote is stopped.
- Request the page. The remote fails to load and the host renders its fallback, as expected.
- Start the remote and request the page again. The host still renders the fallback, with the original error, on every request.
The scenario "SSR hosts that start during an outage render the remotes once they return" in module-federation/tanstack#1 automates this against vite dev and vite preview hosts.
Relevant log output
Repeated on every request after the remote is back:
Error in renderToReadableStream: Error: [ Federation Runtime ]: Failed to load Node.js entry for remote "__mfe_internal__tanstack_vite_host__mf_owner__228093996138680__tanstack_vite_remote" from "http://127.0.0.1:3001/remoteEntry.js". fetch failedProposed fix
Treat a failed attempt as retryable instead of final:
- Once
__mf_remote_pendingrejects, replace it (and__mfRemotePending) with a thenable that starts a new__mfStartRemoteLoad()when the next importer awaits it. This needs the export to beletinstead ofconst. - In development, let the
thenexport resolve after a failed load, with live getters fordefaultand__moduleExportsand a__mf_remote_pendingthat forwards to the current attempt. The runner then caches a value that can still recover.
We patch the generated wrappers this way in @module-federation/tanstack (vite-compat.ts). It builds on #1421, which keeps an unawaited failed load from crashing the process.
Browser wrappers also make a single attempt, but a page reload clears that.
I can open a PR with this change.
Activity