{"record":{"id":"db37aee6263f407c","repo":"facebook/react","slug":"cannot-action-through-a-bridge-that-has-been-sh","errorCode":null,"errorMessage":"Cannot ${action} through a Bridge that has been shut down.","messagePattern":"Cannot (.+?) through a Bridge that has been shut down\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-devtools-shared/src/bridge.js","lineNumber":454,"sourceCode":"    // It is a private method that the bridge ensures is only called at the right times.\n    try {\n      if (this._messageQueue.length) {\n        for (let i = 0; i < this._messageQueue.length; i++) {\n          const {event, payload} = this._messageQueue[i];\n          this._wall.send(event, payload);\n        }\n        this._messageQueue.length = 0;\n      }\n    } finally {\n      // We set this at the end in case new messages are added synchronously above.\n      // They're already handled so they shouldn't queue more flushes.\n      this._scheduledFlush = false;\n    }\n  };\n\n  _assertNotShutdown(action: string): void {\n    if (this._isShutdown) {\n      throw new Error(\n        `Cannot ${action} through a Bridge that has been shut down.`,\n      );\n    }\n  }\n\n  _handleMessage: (message: mixed) => void = message => {\n    // Some Walls share a transport with unrelated messages or legacy DevTools\n    // protocols. A message without an event field does not belong to this Bridge.\n    if (\n      message === null ||\n      typeof message !== 'object' ||\n      !('event' in message)\n    ) {\n      return;\n    }\n\n    const event = message.event;\n    if (typeof event !== 'string' || event.length === 0) {","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-devtools-shared/src/bridge.js#L436-L472","documentation":"The Bridge is single-use: shutdown() sets _isShutdown, emits/sends 'shutdown', removes listeners, detaches from the wall, and flushes the queue. Every public method (addListener, emit, send, and incoming message handling) then calls _assertNotShutdown, which throws 'Cannot <action> through a Bridge that has been shut down.' Using the bridge after teardown is a lifecycle bug in the caller.","triggerScenarios":"Calling bridge.send(...), bridge.emit(...), or bridge.addListener(...) after bridge.shutdown() returned — e.g. an async operation or component cleanup path that still holds a reference; or a wall message arriving after shutdown (action 'receive a message').","commonSituations":"Component unmount order races where a late async task sends after DevTools tears down the bridge; reload of an embedded DevTools that shuts down the old bridge while app code keeps sending; multiple owners of the same bridge instance.","solutions":["Drop all references to the bridge on shutdown; don't send from cleanup paths that may run after teardown.","Track lifecycle yourself: subscribe to the bridge's 'shutdown' event and set a flag checked before every send.","If communication must resume, construct a fresh Bridge over a new wall instead of reusing the shut-down one."],"exampleFix":"// before\nbridge.shutdown();\n// ...later, in an async callback that still holds `bridge`\nbridge.send('logElementToConsole', {id});\n\n// after\nlet isShutdown = false;\nbridge.addListener('shutdown', () => { isShutdown = true; });\nbridge.shutdown();\n// ...later\nif (!isShutdown) {\n  bridge.send('logElementToConsole', {id});\n}","handlingStrategy":"validation","validationCode":"let isShutdown = false;\nbridge.addListener('shutdown', () => {\n  isShutdown = true;\n});\n// before every use:\nif (!isShutdown) bridge.send('myEvent', payload);","typeGuard":null,"tryCatchPattern":"try {\n  bridge.send('myEvent', payload);\n} catch (error) {\n  if (/been shut down/.test(error.message)) return; // stale reference after teardown\n  throw error;\n}","preventionTips":["Clear bridge references when the owning component/store is destroyed.","Listen for the bridge's 'shutdown' event and gate all later calls on it.","Never share one Bridge across owners with independent lifetimes; create a new Bridge to resume."],"tags":["devtools","bridge","lifecycle","shutdown","use-after-dispose"],"backgroundTag":"use-after-dispose","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}