{"record":{"id":"02dc12f20c365575","repo":"facebook/relay","slug":"relaymodernstore-cannot-batch-updates-while-alrea","errorCode":null,"errorMessage":"RelayModernStore: Cannot batch updates while already batching updates.","messagePattern":"RelayModernStore: Cannot batch updates while already batching updates\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/relay-runtime/store/RelayModernStore.js","lineNumber":263,"sourceCode":"    try {\n      this._resolverCache.batchLiveStateUpdates(callback);\n    } finally {\n      if (this.__log != null) {\n        this.__log({name: 'liveresolver.batch.end'});\n      }\n    }\n  }\n\n  /**\n   * Batch multiple store updates into a single notification pass.\n   *\n   * Fragments will still correctly Suspend on their own parent query during\n   * a batch. However, cross-operation Suspense (a fragment suspending on a\n   * *different* in-flight operation) may not work correctly for operations\n   * that complete during the batch.\n   */\n  experimental_batchUpdates(callback: () => void): void {\n    if (this._batch != null) {\n      throw new Error(\n        'RelayModernStore: Cannot batch updates while already batching updates.',\n      );\n    }\n    const log = this.__log;\n    if (log != null) {\n      log({name: 'store.batch.start'});\n    }\n    const batch: Batch = {sourceOperations: [], invalidateStore: false};\n    this._batch = batch;\n    try {\n      callback();\n    } finally {\n      this._batch = null;\n      this.notify(undefined, batch.invalidateStore);\n      for (const sourceOperation of batch.sourceOperations) {\n        this._recordSourceOperation(sourceOperation);\n      }","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/facebook/relay/blob/668b1b85e06261aa3b58dabfc51f8b5524a70955/packages/relay-runtime/store/RelayModernStore.js#L245-L281","documentation":"RelayModernStore only supports a single active batch: experimental_batchUpdates sets this._batch and throws if it is already non-null, because nesting batches could incorrectly resolve cross-operation Suspense and produce inconsistent public-record notifications.","triggerScenarios":"Calling store.experimental_batchUpdates(cb) from inside another experimental_batchUpdates callback — e.g. two library routines both batching during a commit, or an event handler invoked within a batched callback issuing another batched update.","commonSituations":"Composing libraries or middlewares that each call batchUpdates; scheduling batched commits from within store subscribers fired during a batch; React concurrent callbacks overlapping with manual batching.","solutions":["Restructure so batches are not nested — move the inner callback's work into the outer batch","Only batch at the top level of your update logic, not inside subscribers or other batched callbacks","Guard with a flag in your own code if multiple code paths may batch concurrently","Use environment.execute (which handles batching internally via RelayDefaultHandlerProvider commit paths) rather than manual nested batching"],"exampleFix":"// before\nstore.experimental_batchUpdates(() => {\n  store.experimental_batchUpdates(() => updateB());\n});\n// after\nstore.experimental_batchUpdates(() => {\n  updateA();\n  updateB();\n});","handlingStrategy":"try-catch","validationCode":"if (store._batch != null) {\n  callback(); // already batching; run inline\n} else {\n  store.experimental_batchUpdates(callback);\n}","typeGuard":"null","tryCatchPattern":"try {\n  store.experimental_batchUpdates(cb);\n} catch (e) {\n  if (String(e.message).includes('Cannot batch updates while already batching')) {\n    cb(); // or merge into the outer batch\n  } else throw e;\n}","preventionTips":["Only call batchUpdates at top-level app code, never inside subscribers or other batches","Centralize batching in one utility with a re-entrancy guard","Avoid batching inside event handlers triggered during commits","Treat batchUpdates as single-owner API in team conventions"],"tags":["relay","store","batching","reentrancy"],"backgroundTag":"nested-batch-disallowed","analyzedSha":"668b1b85e06261aa3b58dabfc51f8b5524a70955","analyzedAt":"2026-09-02T19:57:20.783Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}