fix(ssr): abort stuck component and in-flight fetch() on hydrate timeout - #6881
Merged
johnjenkins merged 6 commits intoSep 11, 2026
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the current behavior?
GitHub Issue Number: #6864
When
renderToString/hydrateDocumenthitsopts.timeoutthe mock window is destroyed while components can still be mid-await(e.g.componentOnReady()> a component doing afetch()call). Those components then resume against a torn-down window and throw, and the pending promises keep the render's object graph alive - under concurrent load this compounds into unbounded memory growth.What is the new behavior?
Makes
timeoutactually cancels outstanding work instead of just tearing down around it:fetch()calls a component made are aborted rather than left to run to completion (via shimmingfetchto add anAbortController(< whilst still respecting incomingAbortControllers))AbortControlleritself is also shimmed, so anyAbortControllera component creates for its own cancellable work (participating libs include Axios, AWS SDK, the MongoDB Node driver, etc.) is automatically aborted attimeoutFixes #6864
Limitation: for a non-cancellable, truly hung promise, there's nothing Stencil can do - no in-language way exists to force-settle a promise nobody is ever going to resolve, so the object graph it's holding reachable can't be released either.
Documentation
Does this introduce a breaking change?
Testing
Other information
Why not the ceiling / drain approach?
#6865 (the issue raiser preferred solution) waits for pending components to settle before destroying the window, bounded by a second timeout (the same length as the original). To my mind, that's just a double timeout by a different name and kicks the timeout bomb down the road.
It only helps components that finish a bit late (and if that's the case just adjust
opts.timeout). A genuinely stuck component (probably a more likely scenario) hits the identical crash, just now by double the timeout period.Additionally, doubling how long each timed-out render holds resources before releasing makes any memory pressure worse, not better.