@samitouri / QOS-React-2 / commits / b731fe28cc

Improve cyclic thenable detection in ReactFlightReplyServer (#35369)

## Summary This PR improves cyclic thenable detection in `ReactFlightReplyServer.js`. Fixes #35368. The previous fix only detected direct self-references (`inspectedValue === chunk`) and relied on the `cycleProtection` counter to eventually bail out of longer cycles. This change keeps the existing MAX_THENABLE_CYCLE_DEPTH ($1000$) `cycleProtection` cap as a hard guardrail and adds a visited set so that we can detect self-cycles and multi-node cycles as soon as any `ReactPromise` is revisited and while still bounding the amount of work we do for deep acyclic chains via `cycleProtection`. ## How did you test this change? - Ran the existing test suite for the server renderer: ```bash yarn test react-server yarn test --prod react-server yarn flow dom-node yarn linc ``` --------- Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>

Christian Van committed Dec 17, 2025 at 06:22 UTC b731fe28cc492cb36c51c89866f5b63a3ffae2aa
1 file changed +7 -1
packages/react-server/src/ReactFlightReplyServer.js
+7 -1
@@ -133,14 +133,20 @@ ReactPromise.prototype.then = function <T>(
133 // Recursively check if the value is itself a ReactPromise and if so if it points
134 // back to itself. This helps catch recursive thenables early error.
135 let cycleProtection = 0;
136 + const visited = new Set<typeof ReactPromise>();
137 while (inspectedValue instanceof ReactPromise) {
138 cycleProtection++;
138 - if (inspectedValue === chunk || cycleProtection > 1000) {
139 + if (
140 + inspectedValue === chunk ||
141 + visited.has(inspectedValue) ||
142 + cycleProtection > 1000
143 + ) {
144 if (typeof reject === 'function') {
145 reject(new Error('Cannot have cyclic thenables.'));
146 }
147 return;
148 }
149 + visited.add(inspectedValue);
150 if (inspectedValue.status === INITIALIZED) {
151 inspectedValue = inspectedValue.value;
152 } else {