From aad7c664ffbde52e5d8004b542d83d6d4b7a32a0 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Fri, 29 Aug 2025 17:22:39 +0200 Subject: [PATCH] [Flight] Don't try to close debug channel twice (#34340) When the debug channel was already closed, we must not try to close it again when the Response gets garbage collected. **Test plan:** 1. reduce the Flight fixture `App` component to a minimum [^1] - remove everything from `` - delete the `console.log` statement 2. open the app in Firefox (seems to have a more aggressive GC strategy) 3. wait a few seconds On `main`, you will see the following error in the browser console: ``` TypeError: Can not close stream after closing or error ``` With this change, the error is gone. [^1]: It's a bit concerning that step 1 is needed to reproduce the issue. Either GC is behaving differently with the unmodified App, or we may hold on to the Response under certain conditions, potentially creating a memory leak. This needs further investigation. --- packages/react-client/src/ReactFlightClient.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 61a67bce9d803..b966645623bbb 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -1010,10 +1010,15 @@ export function reportGlobalError( if (__DEV__) { const debugChannel = response._debugChannel; if (debugChannel !== undefined) { - // If we don't have any more ways of reading data, we don't have to send any - // more neither. So we close the writable side. + // If we don't have any more ways of reading data, we don't have to send + // any more neither. So we close the writable side. closeDebugChannel(debugChannel); response._debugChannel = undefined; + // Make sure the debug channel is not closed a second time when the + // Response gets GC:ed. + if (debugChannelRegistry !== null) { + debugChannelRegistry.unregister(response); + } } } } @@ -2434,7 +2439,7 @@ function ResponseInstance( // When a Response gets GC:ed because nobody is referring to any of the // objects that lazily load from the Response anymore, then we can close // the debug channel. - debugChannelRegistry.register(this, debugChannel); + debugChannelRegistry.register(this, debugChannel, this); } } }