Skip to content

Fix Suspense and Context from colliding for suspending boundaries - #5303

Open
jacob-ebey wants to merge 3 commits into
preactjs:mainfrom
jacob-ebey:suspense-retry
Open

jacob-ebey wants to merge 3 commits into
preactjs:mainfrom
jacob-ebey:suspense-retry

Conversation

@jacob-ebey

@jacob-ebey jacob-ebey commented Oct 9, 2026 •

Copy link
Copy Markdown

Fixes a Suspense boundary minification collision. mangle.json mapped _id (the identifier on every context object from createContext) to the same minified name (__c) as Suspense's _childDidSuspend.
In built output, the compat _catchError hook finds the nearest boundary by walking up the vnode parents and checking component._childDidSuspend, which becomes component.__c after minification. A component carrying a context's _id, for example when a context object is used as a component, falsely matches. The thrown promise then goes to the wrong component and the real boundary never retries after it resolves.

Comment thread mangle.json
"$_vnode": "__v",
"$_children": "__k",
"$_pendingSuspensionCount": "__u",
"$_childDidSuspend": "__c",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If anything, the other properties will need to be the ones changed; childDidSuspend is used in other repos and changing it would be a big break.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should just change the _id, which is the only possible collision when you use context as a component.

jacob-test-profile and others added 2 commits October 9, 2026 11:54
- Keep _childDidSuspend mangled as __c: other repos (e.g. preact-iso's
  router) rely on that name, so renaming it would be a breaking change.
- Give _id (the identifier on every context object) a unique mangled
  name (__I) instead. It is the only other __c property that can
  realistically appear on a component in the Suspense boundary walk
  (when a context object is used as a component); _cleanup, _component
  and _commit never do.
- Rework the regression test to target the actual cause: it now uses the
  real _id from a real createContext() call instead of a synthetic
  string, and drops the _cleanup case (impossible in practice).

Co-authored-by: jacob-test-profile <jacob-test-profile@users.noreply.github.com>
@jacob-ebey
jacob-ebey marked this pull request as ready for review October 9, 2026 18:59
@JoviDeCroock JoviDeCroock changed the title Fix Suspense boundary minification collision (#1) Fix Suspense and Context from colliding for suspending boundaries Oct 10, 2026
@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 99.857%. remained the same — jacob-ebey:suspense-retry into preactjs:main

@JoviDeCroock JoviDeCroock left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When I saw that preact-render-to-string uses _id I decided to try and reproduce this and to my surprise I could not. Even the test hacks this by doing this._id = Ctx._id. I think the agent coding the test got confused between the constructor and the instance

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants