Skip to content

fix(runtime-core): route manifest validation errors through errorLoadRemote#4654

Open
sessa wants to merge 3 commits intomodule-federation:mainfrom
sessa:fix/error-load-remote-manifest-validation
Open

fix(runtime-core): route manifest validation errors through errorLoadRemote#4654
sessa wants to merge 3 commits intomodule-federation:mainfrom
sessa:fix/error-load-remote-manifest-validation

Conversation

@sessa
Copy link
Copy Markdown

@sessa sessa commented Apr 10, 2026

Description

Moves the manifest validation assert (checking for metaData, exposes, and shared fields) inside the existing try/catch block in SnapshotHandler, so that validation failures are routed through the errorLoadRemote lifecycle hook instead of throwing an uncatchable error.

Additionally wraps the assignRemoteInfo call in the snapshot plugin with error handling that also emits errorLoadRemote, giving plugin authors a consistent recovery point for both fetch failures and malformed manifest payloads.

Related Issue

Manifest validation errors previously bypassed errorLoadRemote, making it impossible for plugins to recover from or handle invalid manifests gracefully.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • I have updated the documentation.

…Remote

Two error paths in the snapshot plugin bypass `errorLoadRemote`, causing
unrecoverable crashes when a remote's manifest is malformed or its
remoteEntry URL is missing. Consumers that register an `errorLoadRemote`
handler expect all remote-loading failures to flow through the hook so
they can provide graceful fallbacks (e.g. rendering a shell without the
failed micro-frontend).

**Bug 1 — manifest shape validation (SnapshotHandler.getManifestJson):**
The `assert(metaData && exposes && shared)` sat outside the `try/catch`
that emits `errorLoadRemote`. When a manifest fetched successfully as
valid JSON but was missing required fields, the assert threw directly,
bypassing the hook entirely.

Fix: move the assert inside the `try` block so the `catch` emits
`errorLoadRemote` with `lifecycle: 'afterResolve'`, giving plugins a
chance to supply a fallback manifest.

**Bug 2 — missing remoteEntry URL (snapshotPlugin.afterResolve):**
`assignRemoteInfo` calls `error(RUNTIME_011)` which throws when
`remoteEntryInfo.url` is empty. This throw was unguarded in the
`afterResolve` hook, so it propagated as an uncaught waterfall error
instead of routing through `errorLoadRemote`.

Fix: wrap `assignRemoteInfo` in a try/catch that emits `errorLoadRemote`
with `lifecycle: 'afterResolve'`. If the hook returns a truthy value the
error is considered handled and the host continues; otherwise the
original error is re-thrown.

Made-with: Cursor
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 10, 2026

Deploy Preview for module-federation-docs ready!

Name Link
🔨 Latest commit 2c5cecf
🔍 Latest deploy log https://app.netlify.com/projects/module-federation-docs/deploys/69d96155d02fc40008715ca2
😎 Deploy Preview https://deploy-preview-4654--module-federation-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 10, 2026

⚠️ No Changeset found

Latest commit: 2c5cecf

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

Copy link
Copy Markdown

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

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: 2f034b5e95

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +301 to +304
assert(
manifestJson.metaData && manifestJson.exposes && manifestJson.shared,
`"${manifestUrl}" is not a valid federation manifest for remote "${moduleInfo.name}". Missing required fields: ${[!manifestJson.metaData && 'metaData', !manifestJson.exposes && 'exposes', !manifestJson.shared && 'shared'].filter(Boolean).join(', ')}.`,
);
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 Validate failover manifests after errorLoadRemote

In getManifestJson, moving the required-field assert entirely inside the fetch try means values returned from errorLoadRemote are now accepted without shape checks. If a plugin returns a non-manifest payload (for example an args object), it gets treated as manifestJson and then fails later in generateSnapshotFromManifest, outside this recovery block, which bypasses the intended afterResolve handling and produces a harder-to-recover error path. Re-validate manifestJson after the catch before caching/using it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 2c5cecf. Added a post-catch assert that re-validates manifestJson after the errorLoadRemote failover, ensuring payloads returned by plugins are checked for required fields (metaData, exposes, shared) before being cached or passed to generateSnapshotFromManifest. Also added a test covering this case.

Comment on lines +64 to +68
if (!failOver) {
throw assignError;
}

return args;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply afterResolve failover instead of discarding it

When assignRemoteInfo throws, this block treats any truthy errorLoadRemote return as handled, but then returns the original args without applying the failover result. That suppresses the original error while leaving remoteInfo unchanged, so loading continues with invalid remote entry metadata and fails later in a different lifecycle. If afterResolve failover is supported here, its return value needs to be consumed to repair state; otherwise the original error should be rethrown.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 2c5cecf. The failover is now passed to assignRemoteInfo(remoteInfo, failOver as ModuleInfo) so the returned snapshot is actually applied to repair remoteInfo state. If the failover itself is invalid, assignRemoteInfo will throw and the original error propagation is preserved.

sessa added 2 commits April 10, 2026 15:38
Update errorLoadRemote docs to reflect that afterResolve lifecycle
now also catches manifest validation errors (missing metaData, exposes,
or shared fields), not just network failures. Update ZH runtime-hooks
to include the lifecycle field and descriptions matching the EN version.

Made-with: Cursor
Address review feedback:
- Re-validate manifestJson after errorLoadRemote returns to reject
  invalid failover payloads before they reach generateSnapshotFromManifest
- Apply failover snapshot via assignRemoteInfo instead of discarding it
- Add test covering invalid failover rejection

Made-with: Cursor
@2heal1
Copy link
Copy Markdown
Member

2heal1 commented Apr 15, 2026

hey , can you help resolve conflicts

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.

2 participants