<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: maneeq</title>
    <description>The latest articles on DEV Community by maneeq (@maneeq).</description>
    <link>https://dev.to/maneeq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4184322%2F4aa84c16-e419-498c-93e3-500a9dfe7672.jpg</url>
      <title>DEV Community: maneeq</title>
      <link>https://dev.to/maneeq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maneeq"/>
    <language>en</language>
    <item>
      <title>The Module Federation bug that passed every build and crashed in the browser</title>
      <dc:creator>maneeq</dc:creator>
      <pubDate>Wed, 14 Oct 2026 10:04:02 +0000</pubDate>
      <link>https://dev.to/maneeq/the-module-federation-bug-that-passed-every-build-and-crashed-in-the-browser-2pp3</link>
      <guid>https://dev.to/maneeq/the-module-federation-bug-that-passed-every-build-and-crashed-in-the-browser-2pp3</guid>
      <description>&lt;p&gt;A remote that hadn't changed started crashing in the test environment. The console said:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ReferenceError: Cannot access '_t' before initialization
    at Module.OrderListComponent (1429.js:1:604)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;_t&lt;/code&gt; is a minified name, &lt;code&gt;1429.js&lt;/code&gt; is a chunk, and the component in the stack trace hadn't changed. Every CI pipeline was green. Every build passed. The app only broke when the shell loaded it next to a brand-new remote from another team.&lt;/p&gt;

&lt;p&gt;It took the better part of a day to find the real error underneath:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: [ Federation Runtime ]: Version 19.2.3 from inventory of shared singleton
module @angular/material/autocomplete does not satisfy the requirement of orders
which needs 19.2.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Names and versions changed. The shape of the bug is real.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;We run an Angular micro-frontend platform on Module Federation. A shell loads several remotes, each built and deployed from its own repo by its own team. Big libraries like Angular, RxJS and the UI kit are shared as &lt;strong&gt;singletons&lt;/strong&gt;: only one copy is loaded, and every remote uses it.&lt;/p&gt;

&lt;p&gt;Two remotes were involved:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Remote&lt;/th&gt;
&lt;th&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Version it ships&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;orders&lt;/code&gt; (old)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"@angular/material": "19.2.1"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19.2.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;inventory&lt;/code&gt; (new)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"@angular/material": "^19.2.0"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;19.2.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's how the federation runtime resolves a shared singleton, using the default strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every loaded participant offers the version it was built with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The highest version wins.&lt;/strong&gt; Here that's 19.2.3, from &lt;code&gt;inventory&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Every participant checks the winner against its own &lt;code&gt;requiredVersion&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;orders&lt;/code&gt; pinned the exact version &lt;code&gt;19.2.1&lt;/code&gt;, so &lt;code&gt;19.2.3&lt;/code&gt; fails its check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With &lt;code&gt;strictVersion&lt;/code&gt; the runtime throws the error above. Without it, &lt;code&gt;orders&lt;/code&gt; silently runs on a version it was never built or tested against. When that happens halfway through loading a module graph, you get errors like &lt;code&gt;Cannot access '_t' before initialization&lt;/code&gt;, which point nowhere near the real cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why nothing caught it
&lt;/h2&gt;

&lt;p&gt;Every check we had looked at &lt;strong&gt;one repo at a time&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;orders&lt;/code&gt; built fine with 19.2.1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inventory&lt;/code&gt; built fine with 19.2.3.&lt;/li&gt;
&lt;li&gt;Unit tests, lint and e2e tests all passed per app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conflict only exists when the two remotes are &lt;strong&gt;combined at runtime in the browser&lt;/strong&gt;, and no step in either pipeline ever looked at that combination.&lt;/p&gt;

&lt;p&gt;The fix took one character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- "@angular/material": "19.2.1",
&lt;/span&gt;&lt;span class="gi"&gt;+ "@angular/material": "^19.2.1",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild &lt;code&gt;orders&lt;/code&gt;, redeploy, done. The day lost to debugging was the expensive part, and a bug that only shows up when two teams' deploys meet will come back in some other form.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data was there all along
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@module-federation/enhanced&lt;/code&gt; (used by Nx, rspack, Rsbuild and Modern.js) writes an &lt;code&gt;mf-manifest.json&lt;/code&gt; next to every &lt;code&gt;remoteEntry.js&lt;/code&gt;. It lists every shared package with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;version&lt;/code&gt; that remote ships&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;requiredVersion&lt;/code&gt; it will accept&lt;/li&gt;
&lt;li&gt;whether it's a &lt;code&gt;singleton&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's everything needed to predict the crash &lt;em&gt;before&lt;/em&gt; it happens. You don't need source access to other teams' repos, only the URLs of their deployed manifests.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built mfe-guard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/mfe-guard" rel="noopener noreferrer"&gt;mfe-guard&lt;/a&gt; is a small CLI that fetches the manifests of your shell and remotes, works out which version of each singleton will &lt;strong&gt;actually win at runtime&lt;/strong&gt;, and fails when any participant won't accept it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx mfe-guard check &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nv"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://app.test.example.com/mf-manifest.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nv"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://orders.test.example.com/mf-manifest.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nv"&gt;inventory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://inventory.test.example.com/mf-manifest.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxufzmgsb7ota0upq0a6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxufzmgsb7ota0upq0a6l.png" alt="mfe-guard output: one singleton-unsatisfied error with the exact fix" width="799" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What it reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;error&lt;/strong&gt;: a singleton's winning version doesn't satisfy someone's &lt;code&gt;requiredVersion&lt;/code&gt; (the bug above), with the exact change to make&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;error&lt;/strong&gt;: different major versions of a shared package, or two remotes using the same federation name (they overwrite each other's modules)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;warn&lt;/strong&gt;: singletons pinned to an exact version, a crash waiting for the next patch release&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;warn&lt;/strong&gt;: &lt;code&gt;@angular/core&lt;/code&gt;, &lt;code&gt;rxjs&lt;/code&gt;, &lt;code&gt;zone.js&lt;/code&gt; or React shared without &lt;code&gt;singleton: true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;info&lt;/strong&gt;: compatible drift, for example "&lt;code&gt;orders&lt;/code&gt; was built on ui-kit 2.13 but will run on 2.17 at runtime". It's not an error, but you probably want to test it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first time I ran it against our real test environment, it flagged exactly that kind of minor drift, something nobody had noticed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it in CI
&lt;/h2&gt;

&lt;p&gt;Add a config file to the shell repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"participants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"manifest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://app.test.example.com/mf-manifest.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"manifest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://orders.test.example.com/mf-manifest.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inventory"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"manifest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://inventory.test.example.com/mf-manifest.json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add one step after a remote deploys to the test environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx mfe-guard check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command exits with code 1 on any error, so the pipeline stops before the combination reaches production. There's also a project mode (&lt;code&gt;-p name=./path&lt;/code&gt;) that reads &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;node_modules&lt;/code&gt; when you don't have manifests, and &lt;code&gt;--json&lt;/code&gt; for other tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my past self
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't pin shared singletons to exact versions.&lt;/strong&gt; With singletons the highest version wins, so an exact pin will eventually break when someone else ships a patch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the deployed combination, not just each repo.&lt;/strong&gt; In micro-frontends the risk sits between the repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep one list of shared versions across teams&lt;/strong&gt;, or at least look at the drift regularly. Patch drift is noise; minor drift is a test you haven't run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;mfe-guard is open source (MIT) and young: v0.1.1, built because of this exact bug. If you run Module Federation in production, I'd love to know whether it catches anything in your setup, or what it misses.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/mfe-guard" rel="noopener noreferrer"&gt;mfe-guard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/maneeq/mfe-guard" rel="noopener noreferrer"&gt;maneeq/mfe-guard&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you hit a Module Federation version bug that only showed up at runtime? I'd like to hear how you tracked it down.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>microfrontends</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
