Skip to content

fix(runtime-core): resolve remote runtime instance identity explicitly during removal - #5029

Open
ScriptedAlchemy wants to merge 2 commits into
mainfrom
fix/runtime-core-remove-remote-instance-cleanup
Open

fix(runtime-core): resolve remote runtime instance identity explicitly during removal#5029
ScriptedAlchemy wants to merge 2 commits into
mainfrom
fix/runtime-core-remove-remote-instance-cleanup

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Found while investigating #4566.

RemoteHandler.removeRemote (called by registerRemotes(remotes, { force: true }) when a name is re-registered) looks up the remote's own runtime instance in __FEDERATION__.__INSTANCES__ so it can release the remote's share-scope entries and splice the instance out. It matched only on the registered name (or name:buildVersion), so whenever the registration alias differed from the name the remote was built with, the instance leaked on every forced re-registration, silently.

This PR makes the identity used for that lookup explicit and monotonic, releases shares by the instance's own name, and only warns when the situation is actually suspicious.

Identity model

A loaded remote is known under five distinct names, and the code now keeps them apart:

Identity Source Used for
registration alias remoteInfo.name host.options.remotes, moduleCache, snapshots
remote build / global name remoteInfo.entryGlobalName container global, instance lookup
runtime instance id instance.options.id __SHARE__ map key
share producer name instance.options.name ?? instance.name shared.from / shared.useIn
consuming host name host.options.name never used for lookup

Matching order (resolveRemoteRuntimeInstance)

New pure helper packages/runtime-core/src/remote/resolveRemoteRuntimeInstance.ts. The first level that yields any candidate wins; a level with more than one candidate returns ambiguous: true and no instance (nothing is guessed):

  1. registered name + buildVersionoptions.id === name:buildVersion, or options.name === name && options.version === buildVersion
  2. entryGlobalName + buildVersion — same two comparisons, only when entryGlobalName differs from the registered name
  3. registered name alone — only when no buildVersion is known
  4. entryGlobalName alone — only when no buildVersion is known and exactly one instance matches

A versioned lookup never degrades into an unversioned name match.

Share release

removeRemote is split into single-purpose private steps run in the previous order under the existing try/catch: removeRemoteRegistration, clearRemoteSnapshots, clearRemoteEntryState (container global, globalLoading, moduleCache), then releaseRuntimeInstanceShares. Share release derives producerName = instance.options.name ?? instance.name and uses it for shared.from; useIn only drops producerName (the remote consuming its own share), never the registration alias. isAllSharedNotUsed / needDeleteKeys semantics and the globalShareScopeMap[instance.options.id || producerName] deletion are unchanged.

Warning

logger.warn fires only when:

  • the match is ambiguous (message names the level and the candidate count), or
  • a versioned lookup found instances with the same registered name / entryGlobalName but none with the requested build version (message lists the versions found).

A container with no runtime instance at all (plain containers) is the legitimate case and stays silent.

Tests

packages/runtime-core/__tests__/register-remotes.spec.ts, mirrored in packages/runtime/__tests__/register-remotes.spec.ts (synthetic instances injected into __INSTANCES__):

  • two instances with the same build name and different build versions: only the matching version is removed
  • registration alias differing from the build name (entryGlobalName = instance name): resolved and removed, no warning
  • unloaded share produced by the remote instance is deleted from the global share scope
  • share still consumed by the host is kept and useIn no longer contains the remote's own name
  • unversioned lookup with two same-named instances: nothing removed, one ambiguity warning
  • same-named instances with a different build version: one warning listing the versions found
  • container without any runtime instance: no warning, no throw, module cache cleared, new entry loads
  • instance whose options.id is custom but whose options.name/options.version match: still resolved

packages/runtime-core/__tests__/resolve-remote-runtime-instance.spec.ts unit-tests every level, the versioned-never-falls-back rule, ambiguity, and entryGlobalName === name not forming a level.

pnpm --filter @module-federation/runtime-core test (142 passed) and pnpm --filter @module-federation/runtime test (101 passed).

A patch changeset for @module-federation/runtime-core is included.

🤖 Generated with Claude Code

…when cleanup misses

removeRemote (used by registerRemotes with force: true) looked up the
remote's runtime instance in __FEDERATION__.__INSTANCES__ only by the
registered name. When that differs from the name the remote was built
with, the lookup silently missed: the container global and module cache
were dropped but the old instance stayed registered, so every forced
re-registration leaked a full container graph.

The lookup now falls back to matching by entryGlobalName (the runtime
instance name for enhanced/webpack-built containers) and logs a warning
when no instance can be found instead of failing silently.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 46e8265

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 48 packages
Name Type
@module-federation/runtime-core Patch
@module-federation/nextjs-mf Patch
@module-federation/runtime Patch
@module-federation/bridge-react Patch
@module-federation/devtools Patch
@module-federation/dts-plugin Patch
@module-federation/esbuild Patch
@module-federation/metro Patch
@module-federation/modern-js-v3 Patch
@module-federation/modern-js Patch
@module-federation/node Patch
@module-federation/observability-plugin Patch
@module-federation/playground Patch
@module-federation/retry-plugin Patch
@module-federation/runtime-tools Patch
@module-federation/webpack-bundler-runtime Patch
@module-federation/bridge-vue3 Patch
website-new Patch
@module-federation/metro-plugin-rnc-cli Patch
@module-federation/metro-plugin-rnef Patch
@module-federation/metro-plugin-rock Patch
shared-tree-shaking-with-server-host Patch
shared-tree-shaking-with-server-provider Patch
@module-federation/rsbuild-plugin Patch
@module-federation/rstest Patch
node-dynamic-remote-new-version Patch
node-dynamic-remote Patch
@module-federation/enhanced Patch
@module-federation/rspack Patch
@module-federation/inject-external-runtime-core-plugin Patch
@module-federation/rspress-plugin Patch
remote5 Patch
remote6 Patch
@module-federation/storybook-addon Patch
shared-tree-shaking-no-server-host Patch
shared-tree-shaking-no-server-provider Patch
@module-federation/cli Patch
create-module-federation Patch
@module-federation/error-codes Patch
@module-federation/managers Patch
@module-federation/manifest Patch
@module-federation/sdk Patch
@module-federation/third-party-dts-extractor Patch
@module-federation/treeshake-frontend Patch
@module-federation/treeshake-server Patch
@module-federation/bridge-react-webpack-plugin Patch
@module-federation/bridge-shared Patch
@module-federation/utilities Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T21:41:15.960543Z 172184f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@module-federation/devtools

pnpm add https://pkg.pr.new/@module-federation/devtools@46e8265

@module-federation/cli

pnpm add https://pkg.pr.new/@module-federation/cli@46e8265

create-module-federation

pnpm add https://pkg.pr.new/create-module-federation@46e8265

@module-federation/dts-plugin

pnpm add https://pkg.pr.new/@module-federation/dts-plugin@46e8265

@module-federation/enhanced

pnpm add https://pkg.pr.new/@module-federation/enhanced@46e8265

@module-federation/error-codes

pnpm add https://pkg.pr.new/@module-federation/error-codes@46e8265

@module-federation/esbuild

pnpm add https://pkg.pr.new/@module-federation/esbuild@46e8265

@module-federation/managers

pnpm add https://pkg.pr.new/@module-federation/managers@46e8265

@module-federation/manifest

pnpm add https://pkg.pr.new/@module-federation/manifest@46e8265

@module-federation/metro

pnpm add https://pkg.pr.new/@module-federation/metro@46e8265

@module-federation/metro-plugin-rnc-cli

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnc-cli@46e8265

@module-federation/metro-plugin-rnef

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rnef@46e8265

@module-federation/metro-plugin-rock

pnpm add https://pkg.pr.new/@module-federation/metro-plugin-rock@46e8265

@module-federation/modern-js

pnpm add https://pkg.pr.new/@module-federation/modern-js@46e8265

@module-federation/modern-js-v3

pnpm add https://pkg.pr.new/@module-federation/modern-js-v3@46e8265

@module-federation/native-federation-tests

pnpm add https://pkg.pr.new/@module-federation/native-federation-tests@46e8265

@module-federation/native-federation-typescript

pnpm add https://pkg.pr.new/@module-federation/native-federation-typescript@46e8265

@module-federation/nextjs-mf

pnpm add https://pkg.pr.new/@module-federation/nextjs-mf@46e8265

@module-federation/node

pnpm add https://pkg.pr.new/@module-federation/node@46e8265

@module-federation/observability-plugin

pnpm add https://pkg.pr.new/@module-federation/observability-plugin@46e8265

@module-federation/playground

pnpm add https://pkg.pr.new/@module-federation/playground@46e8265

@module-federation/retry-plugin

pnpm add https://pkg.pr.new/@module-federation/retry-plugin@46e8265

@module-federation/rsbuild-plugin

pnpm add https://pkg.pr.new/@module-federation/rsbuild-plugin@46e8265

@module-federation/rspack

pnpm add https://pkg.pr.new/@module-federation/rspack@46e8265

@module-federation/rspress-plugin

pnpm add https://pkg.pr.new/@module-federation/rspress-plugin@46e8265

@module-federation/rstest

pnpm add https://pkg.pr.new/@module-federation/rstest@46e8265

@module-federation/runtime

pnpm add https://pkg.pr.new/@module-federation/runtime@46e8265

@module-federation/runtime-core

pnpm add https://pkg.pr.new/@module-federation/runtime-core@46e8265

@module-federation/runtime-tools

pnpm add https://pkg.pr.new/@module-federation/runtime-tools@46e8265

@module-federation/sdk

pnpm add https://pkg.pr.new/@module-federation/sdk@46e8265

@module-federation/storybook-addon

pnpm add https://pkg.pr.new/@module-federation/storybook-addon@46e8265

@module-federation/third-party-dts-extractor

pnpm add https://pkg.pr.new/@module-federation/third-party-dts-extractor@46e8265

@module-federation/treeshake-frontend

pnpm add https://pkg.pr.new/@module-federation/treeshake-frontend@46e8265

@module-federation/treeshake-server

pnpm add https://pkg.pr.new/@module-federation/treeshake-server@46e8265

@module-federation/typescript

pnpm add https://pkg.pr.new/@module-federation/typescript@46e8265

@module-federation/utilities

pnpm add https://pkg.pr.new/@module-federation/utilities@46e8265

@module-federation/webpack-bundler-runtime

pnpm add https://pkg.pr.new/@module-federation/webpack-bundler-runtime@46e8265

@module-federation/bridge-react

pnpm add https://pkg.pr.new/@module-federation/bridge-react@46e8265

@module-federation/bridge-react-webpack-plugin

pnpm add https://pkg.pr.new/@module-federation/bridge-react-webpack-plugin@46e8265

@module-federation/bridge-shared

pnpm add https://pkg.pr.new/@module-federation/bridge-shared@46e8265

@module-federation/bridge-vue3

pnpm add https://pkg.pr.new/@module-federation/bridge-vue3@46e8265

@module-federation/inject-external-runtime-core-plugin

pnpm add https://pkg.pr.new/@module-federation/inject-external-runtime-core-plugin@46e8265

commit: 46e8265

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Report

11 package(s) changed, 32 unchanged.

Package dist + ESM entry

Package Total dist (raw) Delta ESM gzip Delta
@module-federation/bridge-vue3 195.5 kB +3.8 kB (+2.0%) 28.0 kB +632 B (+2.3%)
@module-federation/dts-plugin 335.6 kB +182 B (+0.1%) 4.7 kB no change
@module-federation/playground 28.92 MB +10.4 kB (+0.0%) 45.7 kB no change
@module-federation/runtime-core 316.8 kB +6.3 kB (+2.0%) 570 B no change
@module-federation/runtime-tools 7.8 kB +65 B (+0.8%) 142 B no change

Bundle targets

Package Web bundle (gzip) Delta Node bundle (gzip) Delta
@module-federation/bridge-vue3 20.6 kB +585 B (+2.9%) 20.7 kB +581 B (+2.8%)
@module-federation/cli 2.3 kB +1 B (+0.0%) 2.4 kB -33 B (-1.3%)
@module-federation/core 1.0 kB -1 B (-0.1%) 1.0 kB -33 B (-3.0%)
@module-federation/devtools 30.3 kB -6 B (-0.0%) 30.3 kB -30 B (-0.1%)
@module-federation/enhanced 2.7 kB +9 B (+0.3%) 2.8 kB -43 B (-1.5%)
@module-federation/metro-plugin-rnc-cli 416 B -1 B (-0.2%) 435 B -25 B (-5.4%)
@module-federation/node 9.1 kB no change 9.2 kB -29 B (-0.3%)
@module-federation/runtime-core 16.8 kB +606 B (+3.7%) 16.4 kB +600 B (+3.7%)

Consumer scenarios

Scenario Web output (gzip) Delta Node output (gzip) Delta Gap (node-web) Delta
Enhanced remoteEntry 22.8 kB +555 B (+2.4%) 24.4 kB +572 B (+2.3%) +1.6 kB +17 B

Total dist (raw): 36.18 MB (+20.9 kB (+0.1%))
Total ESM gzip: 112.1 kB (+632 B (+0.6%))
Total web bundle (gzip): 254.2 kB (+1.2 kB (+0.5%))
Total node bundle (gzip): 256.5 kB (+988 B (+0.4%))
Tracked ./bundler entry gzip: 563 B (no change)
Tracked ./bundler web bundle (gzip): 4.9 kB (no change)
Tracked ./bundler node bundle (gzip): 4.9 kB (no change)

Bundle sizes are generated with rslib (Rspack). Package-root metrics preserve the historical report. Tracked subpath exports such as ./bundler are measured separately so ENV_TARGET-driven tree-shaking is visible. Bare imports are externalized to keep package-level sizes consistent, and assets are emitted as resources.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 172184f711

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +782 to +784
remoteInsIndex = instances.findIndex(
(ins) => ins.name === entryGlobalName,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match the exact built instance instead of entryGlobalName

When an aliased remote uses a custom library.name, entryGlobalName is that library global rather than the Module Federation build name used by the runtime instance; this lookup therefore misses the intended instance and can splice an unrelated instance whose name happens to equal the library global. It also ignores buildVersion, so multiple stale builds with the same name can cause the first, wrong version to be removed. Match the instance using the remote's actual build name and versioned ID instead of treating entryGlobalName as an instance identity.

Useful? React with 👍 / 👎.

Comment on lines 786 to 788
if (remoteInsIndex !== -1) {
const remoteIns =
CurrentGlobal.__FEDERATION__.__INSTANCES__[remoteInsIndex];
const remoteIns = instances[remoteInsIndex];
remoteInsId = remoteIns.options.id || remoteInsId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clean aliased shares using the matched build name

When this fallback successfully finds an aliased remote, its shared entries are registered with from and useIn values based on the remote instance's build name, but the cleanup below still compares and filters using the registered alias in remoteInfo.name. Consequently none of those entries are queued for deletion before the instance is spliced, leaving stale factories in the host share scope that can be selected after the replacement remote loads; derive the cleanup name from the matched remoteIns.

Useful? React with 👍 / 👎.

@ScriptedAlchemy

Copy link
Copy Markdown
Member Author

Context from #4566: removeRemote located the remote's runtime instance only by exact name. When the registered name and the build name differ, the instance stays in __FEDERATION__.__INSTANCES__ while the container and module cache are dropped, so each forced re-registration re-executes the entry and leaves one more instance (and its whole module graph) behind, with no warning. The balanced baseline this restores, measured with matching names on runtime 2.8.2, 2.2.2 and 0.2.5:

force path

…icitly during removal

Introduce resolveRemoteRuntimeInstance with monotonic matching
(registered name + buildVersion, entryGlobalName + buildVersion, then
name-only levels only when no buildVersion is known), split removeRemote
into single-purpose steps, release shares by the instance's own
producer name, and warn only on ambiguous or version-mismatched lookups.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ScriptedAlchemy ScriptedAlchemy changed the title fix(runtime-core): match remote instance by entryGlobalName and warn when cleanup misses fix(runtime-core): resolve remote runtime instance identity explicitly during removal Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant